Умни указатели

29 октомври 2024

Административни неща

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Smart pointers

Sized

Sized

Sized

?Sized ???

?Sized ???

?Sized ???

?Sized ???

?Sized ???

?Sized означава, че типа не е нужно да имплементира Sized.

1 2 3 4 5 6
// Използваем само с тип, който имплементира Sized:
fn foo<T>() {}

// Използваем с тип, който *може* да имплементира Sized,
// но не е *нужно*:
fn bar<T: ?Sized>() {}

Особено ограничение, понеже разширява броя типове, които могат да се приемат, вместо да го стеснява.

Защо? Защото ако една стойност не е Sized, компилатора не може да я алокира на стека. Така че е доста добра идея да присъства като автоматичен trait bound.

?Sized ???

1 2 3 4 5
fn foo<T>(t: T) {}            // -> totally fine, T е Sized

fn bar<T: ?Sized>(t: &T) {}   // -> totally fine, &T е Sized

// fn bar<T: ?Sized>(t: T) {} // -> невъзможно, защото как ще се алокира t?

Smart pointers

&T

Най-глупавия указател, но важен за целите на сравнението

&T

&T

&T

&T

&T: Директен достъп до памет

1 2 3 4 5 6 7 8 9 10 11 12 13
let potato = String::from("
    Любов, любов, варен картоф,
    разрежеш го, а той суров.
");

let lines = potato
    .trim()
    .lines()
    .map(|l| l.trim());

for line in lines {
    println!("{}", line);
}
Любов, любов, варен картоф, разрежеш го, а той суров.
fn main() {
let potato = String::from("
    Любов, любов, варен картоф,
    разрежеш го, а той суров.
");

let lines = potato
    .trim()
    .lines()
    .map(|l| l.trim());

for line in lines {
    println!("{}", line);
}
}

В горния пример има само алокация на първия String, останалите методи -- trim, lines, map, само алокират малки стойности на стека.

Box

Reference + ownership!

Box

1 2 3 4
fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
}
b = 5
fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
}

Box

Box

Box

Box

Box

Box

1 2 3 4 5 6
fn main() {
    let x = Box::new(3);
    let y = Box::new(5);

    println!("{}", x + y);
}
error[E0369]: cannot add `Box<{integer}>` to `Box<{integer}>` --> src/bin/main_81c11fbe09e1559f40de38f65b4627444769a9b5.rs:5:22 | 5 | println!("{}", x + y); | - ^ - Box<{integer}> | | | Box<{integer}> | note: the foreign item type `Box<{integer}>` doesn't implement `Add<Box<{integer}>>` --> /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/alloc/src/boxed.rs:235:1 ::: /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/alloc/src/boxed.rs:238:1 | = note: not implement `Add<Box<{integer}>>` For more information about this error, try `rustc --explain E0369`. error: could not compile `rust` (bin "main_81c11fbe09e1559f40de38f65b4627444769a9b5") due to 1 previous error
fn main() {
    let x = Box::new(3);
    let y = Box::new(5);

    println!("{}", x + y);
}

Box

1 2 3 4 5 6 7 8 9 10 11
fn main() {
    let x = Box::new(3);
    let y = Box::new(5);

    println!("{}", *x + *y);

    let x = &3;
    let y = &5;

    println!("{}", *x + *y);
}
8 8
fn main() {
    let x = Box::new(3);
    let y = Box::new(5);

    println!("{}", *x + *y);

    let x = &3;
    let y = &5;

    println!("{}", *x + *y);
}

(Note: това не е специално за Box -- ще видим как работи * след малко)

Box

А за какво ни е всъщност?

Box

Linked list без Box

1 2 3 4 5 6 7 8 9 10 11 12 13
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, List),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Cons(2, Cons(3, Nil)));

    println!("{:#?}", list);
}
error[E0072]: recursive type `List` has infinite size --> src/bin/main_8ba92f92a6708ff40e65364f4344a56ecfa46195.rs:2:1 | 2 | enum List { | ^^^^^^^^^ 3 | Nil, 4 | Cons(i32, List), | ---- recursive without indirection | help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle | 4 | Cons(i32, Box<List>), | ++++ + error[E0391]: cycle detected when computing when `List` needs drop --> src/bin/main_8ba92f92a6708ff40e65364f4344a56ecfa46195.rs:2:1 | 2 | enum List { | ^^^^^^^^^ | = note: ...which immediately requires computing when `List` needs drop again = note: cycle used when computing whether `List` needs drop = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information Some errors have detailed explanations: E0072, E0391. For more information about an error, try `rustc --explain E0072`. error: could not compile `rust` (bin "main_8ba92f92a6708ff40e65364f4344a56ecfa46195") due to 2 previous errors
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, List),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Cons(2, Cons(3, Nil)));

    println!("{:#?}", list);
}

Box

Box има фиксиран размер на стека, така че няма проблеми.

1 2 3 4 5 6 7 8 9 10 11 12
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, Box<List>),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("{:?}", list);
}
Cons(1, Cons(2, Cons(3, Nil)))
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, Box),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("{:?}", list);
}

Box

Можем ли вместо Box да ползваме &? Kinda:

1 2 3 4 5 6 7 8 9 10 11 12 13
#[derive(Debug)]
enum List<'a> {
    Nil,
    Cons(i32, &'a List<'a>),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, &Cons(2, &Nil));

    println!("{:?}", list);
}
Cons(1, Cons(2, Nil))
#[derive(Debug)]
enum List<'a> {
    Nil,
    Cons(i32, &'a List<'a>),
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, &Cons(2, &Nil));

    println!("{:?}", list);
}

Box

Това работи, но не можем да местим тази стойност:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#[derive(Debug)]
enum List<'a> {
    Nil,
    Cons(i32, &'a List<'a>),
}

use List::{Cons, Nil};

fn return_list<'a>(x: i32, y: i32) -> List<'a> {
    let list1 = Nil;
    let list2 = Cons(y, &list1);
    Cons(x, &list2)
}

fn main() {
    println!("{:?}", return_list(1, 2));
}
error[E0515]: cannot return value referencing local variable `list2` --> src/bin/main_784ae169fd7da4be02bf7925fdc77105c9df81ec.rs:12:5 | 12 | Cons(x, &list2) | ^^^^^^^^------^ | | | | | `list2` is borrowed here | returns a value referencing data owned by the current function error[E0515]: cannot return value referencing local variable `list1` --> src/bin/main_784ae169fd7da4be02bf7925fdc77105c9df81ec.rs:12:5 | 11 | let list2 = Cons(y, &list1); | ------ `list1` is borrowed here 12 | Cons(x, &list2) | ^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function For more information about this error, try `rustc --explain E0515`. error: could not compile `rust` (bin "main_784ae169fd7da4be02bf7925fdc77105c9df81ec") due to 2 previous errors
#[derive(Debug)]
enum List<'a> {
    Nil,
    Cons(i32, &'a List<'a>),
}

use List::{Cons, Nil};

fn return_list<'a>(x: i32, y: i32) -> List<'a> {
    let list1 = Nil;
    let list2 = Cons(y, &list1);
    Cons(x, &list2)
}

fn main() {
    println!("{:?}", return_list(1, 2));
}

Box

Никакъв проблем ако ползваме Box, защото Cons(1, Box::new(...)) си държи ownership:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, Box<List>),
}

use List::{Cons, Nil};

fn return_list(x: i32, y: i32) -> List {
    let list1 = Box::new(Nil);
    let list2 = Cons(y, list1);
    Cons(x, Box::new(list2))
}

fn main() {
    println!("{:?}", return_list(1, 2));
}
Cons(1, Cons(2, Nil))
#[derive(Debug)]
enum List {
    Nil,
    Cons(i32, Box),
}

use List::{Cons, Nil};

fn return_list(x: i32, y: i32) -> List {
    let list1 = Box::new(Nil);
    let list2 = Cons(y, list1);
    Cons(x, Box::new(list2))
}

fn main() {
    println!("{:?}", return_list(1, 2));
}

Box

Trait objects (преговор)

Ако имаме trait Stuff, &dyn Stuff представлява какъвто и да е обект имплементиращ trait-а.

1 2 3 4 5 6 7 8 9 10 11
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

fn main() {
    let trait_object: &dyn ToJson = &5;

    println!("{}", to_json(trait_object));
    println!("{}", to_json(&5));
    println!("{}", to_json(&5 as &dyn ToJson));
}
5 5 5
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

fn main() {
    let trait_object: &dyn ToJson = &5;

    println!("{}", to_json(trait_object));
    println!("{}", to_json(&5));
    println!("{}", to_json(&5 as &dyn ToJson));
}

Box

Trait objects

1 2 3 4
fn vec_of_things<'a>() -> Vec<&'a dyn Display> {
    let x = 123;
    vec![&x, &3.14, &"foobar"]
}
error[E0515]: cannot return value referencing local variable `x` --> src/bin/main_6ccf239cdd09201f91ae142543011bacb499f05c.rs:4:5 | 4 | vec![&x, &3.14, &"foobar"] | ^^^^^--^^^^^^^^^^^^^^^^^^^ | | | | | `x` is borrowed here | returns a value referencing data owned by the current function | = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0515`. error: could not compile `rust` (bin "main_6ccf239cdd09201f91ae142543011bacb499f05c") due to 1 previous error
use std::fmt::Display;
fn vec_of_things<'a>() -> Vec<&'a dyn Display> {
    let x = 123;
    vec![&x, &3.14, &"foobar"]
}
fn main() {}

Box

Trait objects

1 2 3 4 5 6 7 8 9 10
fn vec_of_things() -> Vec<Box<dyn Display>> {
    let x = 123;
    vec![Box::new(x), Box::new(3.14), Box::new("foobar")]
}

fn main() {
    for thing in vec_of_things() {
        println!("{}", thing);
    }
}
123 3.14 foobar
use std::fmt::Display;
fn vec_of_things() -> Vec> {
    let x = 123;
    vec![Box::new(x), Box::new(3.14), Box::new("foobar")]
}

fn main() {
    for thing in vec_of_things() {
        println!("{}", thing);
    }
}

Box

Box<Error> го споменахме вече -- ако ни мързи да правим error handling

1 2 3 4 5 6 7 8 9 10 11 12
fn get_x() -> Result<i32, std::io::Error> { Ok(3) }
fn get_y() -> Result<i32, std::fmt::Error> { Ok(5) }

fn foo() -> Result<i32, Box<dyn std::error::Error>> {
    let x = get_x()?;
    let y = get_y()?;
    Ok(x + y)
}

fn main() {
    println!("{:?}", foo());
}
Ok(8)
fn get_x() -> Result { Ok(3) }
fn get_y() -> Result { Ok(5) }

fn foo() -> Result> {
    let x = get_x()?;
    let y = get_y()?;
    Ok(x + y)
}

fn main() {
    println!("{:?}", foo());
}

Това рядко ще е проблем откъм performance.

Box

Box и съпоставяне на образци

Типа Box<Term> не може да се pattern-match-не по компоненти -- вътрешността му е private.

1 2 3 4 5 6 7
#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    True,
    False,
    If(Box<Term>, Box<Term>, Box<Term>),
    Value
}
#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    True,
    False,
    If(Box, Box, Box),
    Value
}
fn main() {}

Box

Box и съпоставяне на образци

Типа Box<Term> не може да се pattern-match-не по компоненти -- вътрешността му е private.

1 2 3 4 5 6 7 8 9 10 11 12
fn one_step_eval(t: Term) -> Result<Box<Term>, String> {
    match t {
        // не може Term::If(Term::True, t2, _) => Ok(t2)
        Term::If(t1, t2, _) if *t1 == Term::True => Ok(t2),
        // не може Term::If(Term::False, _, t3) => Ok(t3)
        Term::If(t1, _, t3) if *t1 == Term::False => Ok(t3),

        Term::If(t1, t2, t3) => Ok(Box::new(Term::If(one_step_eval(*t1)?, t2, t3))),

        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}
#[derive(Clone, Debug, PartialEq)]
pub enum Term {
True,
False,
If(Box, Box, Box),
Value
}
fn one_step_eval(t: Term) -> Result, String> {
    match t {
        // не може Term::If(Term::True, t2, _) => Ok(t2)
        Term::If(t1, t2, _) if *t1 == Term::True => Ok(t2),
        // не може Term::If(Term::False, _, t3) => Ok(t3)
        Term::If(t1, _, t3) if *t1 == Term::False => Ok(t3),

        Term::If(t1, t2, t3) => Ok(Box::new(Term::If(one_step_eval(*t1)?, t2, t3))),

        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}
fn main() {}

Box

Box и съпоставяне на образци

Box

Box и съпоставяне на образци

Box

Box и съпоставяне на образци

Deref

Deref

Как работи * при нормалните references?

1 2 3 4 5 6 7 8
let mut x = 5;
{
    let y = &mut x;

    *y += 1;
    println!("y = {}", y);
}
println!("x = {}", x);
y = 6 x = 6
fn main() {
let mut x = 5;
{
    let y = &mut x;

    *y += 1;
    println!("y = {}", y);
}
println!("x = {}", x);
}

Може да достъпим стойността зад reference-а чрез * и да извършим някаква операция върху нея.

Deref

1 2 3 4 5 6 7 8
let x = 5;
{
    let mut y = Box::new(x);

    *y += 1;
    println!("y = {}", y);
}
println!("x = {}", x);
y = 6 x = 5
fn main() {
let x = 5;
{
    let mut y = Box::new(x);

    *y += 1;
    println!("y = {}", y);
}
println!("x = {}", x);
}

Това работи и за Box-нати стойности, защото за типа Box е имплементиран trait-а Deref.

Deref

1 2 3 4
pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

Нужно е ?Sized, понеже така може да имплементираме Deref за не-Sized тип. От стандартната библиотека:

Deref

1 2 3 4
pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

Нужно е ?Sized, понеже така може да имплементираме Deref за не-Sized тип. От стандартната библиотека:

1 2 3 4 5 6 7 8
impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

Тук типа Target е str, който не е Sized. Но &str тотално е Sized и можем да върнем &Target като резултат.

Метода deref не връща директно Target, защото това би ни дало ownership над стойността и в случая на Box<T>, това би преместило стойността и би направило инстанцията неизползваема.

DerefMut

1 2 3
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Забележете липсата на декларация на Target -- на практика се случва <Self as Deref>::Target.

Deref

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use std::ops::Deref;

struct Mp3 {
    audio: Vec<u8>,
    artist: Option<String>,
    title: Option<String>,
}

impl Deref for Mp3 {
    type Target = Vec<u8>;

    fn deref(&self) -> &Vec<u8> {
        &self.audio
    }
}
#![allow(dead_code)]
use std::ops::Deref;

struct Mp3 {
    audio: Vec,
    artist: Option,
    title: Option,
}

impl Deref for Mp3 {
    type Target = Vec;

    fn deref(&self) -> &Vec {
        &self.audio
    }
}
fn main() {}

Deref

1 2 3 4 5 6 7 8 9
fn main() {
    let appropriately_named_song = Mp3 {
        audio: vec![1, 2, 3],
        artist: Some(String::from("Poets of the Fall")),
        title: Some(String::from("Carnival of Rust")),
    };

    assert_eq!(vec![1, 2, 3], *appropriately_named_song);
}
#![allow(dead_code)]
use std::ops::Deref;
struct Mp3 {
audio: Vec,
artist: Option,
title: Option,
}
impl Deref for Mp3 {
type Target = Vec;
fn deref(&self) -> &Vec {
&self.audio
}
}
fn main() {
    let appropriately_named_song = Mp3 {
        audio: vec![1, 2, 3],
        artist: Some(String::from("Poets of the Fall")),
        title: Some(String::from("Carnival of Rust")),
    };

    assert_eq!(vec![1, 2, 3], *appropriately_named_song);
}

Deref

Как се "разгъва" този код от компилатора:

1 2 3 4 5 6 7 8 9 10 11
*appropriately_named_song // -> Компилатора вижда Deref &Mp3 -> &Vec

*(appropriately_named_song.deref())

// Имплементацията връща &self.audio
fn deref(&self) -> &Vec<u8> {
    &self.audio
}

// Разпънатия код е *& от вътрешното `audio`
*(&appropriately_named_song.audio)

Deref

Deref

Deref

Deref

Deref

1 2 3 4 5
impl<'a, T: ?Sized> Deref for &'a T {
    type Target = T;

    fn deref(&self) -> &T { *self }
}

Въпрос: защо *self?

Deref

1 2 3 4 5
impl<'a, T: ?Sized> Deref for &'a T {
    type Target = T;

    fn deref(&self) -> &T { *self }
}

Въпрос: защо *self?

Защото &self е от тип &Self, което в случая е &&T. Така че *self е от тип &T!

DerefMut

Така работи Box -- викаме му *y += 1:

1 2 3 4 5 6 7 8
//
//
fn main() {
    let mut y: Box<u32> = Box::new(5); // Note: mutable

    *y += 1;
    println!("{}", y);
}
6
//
fn main() {
    let mut y: Box = Box::new(5); // Note: mutable

    *y += 1;
    println!("{}", y);
}

DerefMut

Това се превежда до *DerefMut::deref_mut(&mut y) += 1

1 2 3 4 5 6 7 8
use std::ops::DerefMut;

fn main() {
    let mut y: Box<u32> = Box::new(5); // Note: mutable

    *(DerefMut::deref_mut(&mut y)) += 1; //*(&mut u32)
    println!("{}", y);
}
6
use std::ops::DerefMut;

fn main() {
    let mut y: Box = Box::new(5); // Note: mutable

    *(DerefMut::deref_mut(&mut y)) += 1; //*(&mut u32)
    println!("{}", y);
}

Deref

deref coercion

Автоматично се вика Deref не само при *, но и при викане на функции. Примерно, можем да извикаме директно .audio.as_slice():

1 2 3 4 5
fn compress_mp3(audio: &[u8]) -> Vec<u8> {
    // ...
}

compress_mp3(appropriately_named_song.audio.as_slice())

Deref

deref coercion

Но можем и да подадем нещо, на което Deref ще докара правилния тип:

1 2 3 4 5 6 7 8 9 10 11 12
fn compress_mp3(audio: &[u8]) -> Vec<u8> {
    // ...
}

compress_mp3(appropriately_named_song.audio.as_slice())
// &Vec<u8> -> &[u8]
compress_mp3(&appropriately_named_song.audio)
// &Mp3 -> &Vec<u8>
compress_mp3(&appropriately_named_song)

// Става и без викане на функция:
let song_bytes: &[u8] = &appropriately_named_song;

Deref

deref coercion

1 2 3 4 5 6 7
fn compress_mp3(audio: &[u8]) -> Vec<u8> {
    // ...
}

// Еквивалентни:
compress_mp3(&appropriately_named_song)
compress_mp3(&appropriately_named_song.deref().deref())

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

Deref

Rc

Reference counter

Reference counting

1 2 3 4 5 6 7 8 9
use std::rc::Rc;

fn main() {
    let first = Rc::new(String::from("foobar"));
    let second = Rc::clone(&first);

    println!("{}", first);
    println!("{}", second);
}
foobar foobar
use std::rc::Rc;

fn main() {
    let first = Rc::new(String::from("foobar"));
    let second = Rc::clone(&first);

    println!("{}", first);
    println!("{}", second);
}

Reference counting

1 2 3 4
let a = Rc::new(3);
let b = Rc::new(5);

println!("{}", *a + *b);
8
use std::rc::Rc;
fn main() {
let a = Rc::new(3);
let b = Rc::new(5);

println!("{}", *a + *b);
}

Reference counting

Reference counting

Reference counting

Reference counting

Reference counting

Проблем: стойността е read-only:

1 2 3 4 5
let mut a = Rc::new(3);

*a = 5;

println!("{:?}", a);
error[E0594]: cannot assign to data in an `Rc` --> src/bin/main_daef8d7d55ceabff0dc2ebe0ac97186bb40b3b39.rs:5:1 | 5 | *a = 5; | ^^^^^^ cannot assign | = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<i32>` For more information about this error, try `rustc --explain E0594`. error: could not compile `rust` (bin "main_daef8d7d55ceabff0dc2ebe0ac97186bb40b3b39") due to 1 previous error
use std::rc::Rc;
fn main() {
let mut a = Rc::new(3);

*a = 5;

println!("{:?}", a);
}

Reference counting

Reference counting

Reference counting

Clone-on-write

Clone-on-write

1 2 3
impl<T> Rc<T> where T: Clone {
    fn make_mut(this: &mut Rc<T>) -> &mut T
}

Clone-on-write

1 2 3
impl<T> Rc<T> where T: Clone {
    fn make_mut(this: &mut Rc<T>) -> &mut T
}

Clone-on-write

1 2 3
impl<T> Rc<T> where T: Clone {
    fn make_mut(this: &mut Rc<T>) -> &mut T
}

Clone-on-write

1 2 3 4 5 6 7 8 9
use std::rc::Rc;

fn main() {
    let mut a = Rc::new(3);

    *Rc::make_mut(&mut a) = 5;

    println!("a: {}", a);
}
a: 5
use std::rc::Rc;

fn main() {
    let mut a = Rc::new(3);

    *Rc::make_mut(&mut a) = 5;

    println!("a: {}", a);
}

Clone-on-write

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
use std::rc::Rc;

fn main() {
    let mut a = Rc::new(3);
    let b = Rc::clone(&a);
    // Дотук a и b сочат към една и съща стойност в паметта

    {
        let temp_ref = Rc::make_mut(&mut a);
        // Връщаме &mut към copy-on-write стойност
        *temp_ref = 5;
    }
    // Вече a и b сочат към различни стойности

    println!("a: {}", a);
    println!("b: {}", b);
}
a: 5 b: 3
use std::rc::Rc;

fn main() {
    let mut a = Rc::new(3);
    let b = Rc::clone(&a);
    // Дотук a и b сочат към една и съща стойност в паметта

    {
        let temp_ref = Rc::make_mut(&mut a);
        // Връщаме &mut към copy-on-write стойност
        *temp_ref = 5;
    }
    // Вече a и b сочат към различни стойности

    println!("a: {}", a);
    println!("b: {}", b);
}

std::borrow::Cow

ами другите крави?

1 2 3 4 5 6 7
pub enum Cow<'a, T>
where
    T: 'a + ToOwned + ?Sized,
{
    Borrowed(&'a T),
    Owned(<T as ToOwned>::Owned),
}
fn main() {}
pub enum Cow<'a, T>
where
    T: 'a + ToOwned + ?Sized,
{
    Borrowed(&'a T),
    Owned(::Owned),
}

std::borrow::Cow

Пример - String::from_utf8_lossy(v: &[u8]) -> Cow<'_, str>

1 2 3 4 5 6 7 8 9 10 11
let sparkle_heart = String::from_utf8_lossy(&[240, 159, 146, 150]);
match sparkle_heart { // Cow::Borrowed(&str)
    Cow::Borrowed(s) => println!("Borrowed {:?}", s),
    Cow::Owned(s) => println!("Owned {:?}", s),
}

let hello = String::from_utf8_lossy(b"Hello \xF0\x90\x80World");
match hello { // Cow::Owned(String)
    Cow::Borrowed(s) => println!("Borrowed {:?}", s),
    Cow::Owned(s) => println!("Owned {:?}", s),
}
Borrowed "💖" Owned "Hello �World"
use std::borrow::Cow;
fn main() {
let sparkle_heart = String::from_utf8_lossy(&[240, 159, 146, 150]);
match sparkle_heart { // Cow::Borrowed(&str)
    Cow::Borrowed(s) => println!("Borrowed {:?}", s),
    Cow::Owned(s) => println!("Owned {:?}", s),
}

let hello = String::from_utf8_lossy(b"Hello \xF0\x90\x80World");
match hello { // Cow::Owned(String)
    Cow::Borrowed(s) => println!("Borrowed {:?}", s),
    Cow::Owned(s) => println!("Owned {:?}", s),
}
}

Друг вариант: Internal mutability

Друг вариант: Internal mutability

Друг вариант: Internal mutability

Cell, RefCell

Internal mutability

1 2 3 4 5 6 7 8 9 10 11
use std::cell::Cell;

fn main() {
    // забележете, че няма `mut`
    let cell = Cell::new(10);

    println!("{}", cell.get());

    cell.set(42);
    println!("{}", cell.get());
}
10 42
use std::cell::Cell;

fn main() {
    // забележете, че няма `mut`
    let cell = Cell::new(10);

    println!("{}", cell.get());

    cell.set(42);
    println!("{}", cell.get());
}

Internal mutability

Cell

Internal mutability

Cell

Internal mutability

Cell

Internal mutability

Cell

Internal mutability

RefCell

1 2 3 4 5 6 7 8 9 10
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(String::from("foo"));   // отново няма `mut`
    println!("{}", cell.borrow()); // -> Ref<String>

    cell.borrow_mut().push_str("bar"); // -> RefMut<String>

    println!("{}", cell.borrow()); // -> Ref<String>
}
foo foobar
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(String::from("foo"));   // отново няма `mut`
    println!("{}", cell.borrow()); // -> Ref

    cell.borrow_mut().push_str("bar"); // -> RefMut

    println!("{}", cell.borrow()); // -> Ref
}

Internal mutability

RefCell

1 2 3 4 5 6 7 8
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(String::from("foo"));   // отново няма `mut`

    let mut first = cell.borrow_mut();
    let mut second = cell.borrow_mut(); // BOOM!
}
thread 'main' panicked at src/bin/main_45bb39151537827e2a223fcdd3995ba26df346ef.rs:9:27: already borrowed: BorrowMutError note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
#![allow(unused_variables)]
#![allow(unused_mut)]
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(String::from("foo"));   // отново няма `mut`

    let mut first = cell.borrow_mut();
    let mut second = cell.borrow_mut(); // BOOM!
}

Internal mutability

RefCell

Internal mutability

RefCell

Internal mutability

RefCell

Internal mutability

RefCell

Internal mutability

RefCell

Internal mutability

Често Cell и RefCell се използват в комбинация с Rc

1 2 3 4 5 6 7 8 9 10 11
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let first = Rc::new(RefCell::new(String::from("foo")));
    let second = Rc::clone(&first);

    first.borrow_mut().push_str("bar");

    println!("{}", second.borrow());
}
foobar
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let first = Rc::new(RefCell::new(String::from("foo")));
    let second = Rc::clone(&first);

    first.borrow_mut().push_str("bar");

    println!("{}", second.borrow());
}

Cell, RefCell

Cell, RefCell

Cell, RefCell

Cell, RefCell

Обратно към Rc

Weak reference

Какво правим, когато структурата ни може да има цикли?

1 2 3 4 5 6 7 8 9 10 11
struct TreeNode {
    value: u32,
    parent: Option<Rc<RefCell<TreeNode>>>,
    children: Vec<Rc<RefCell<TreeNode>>>,
}

impl TreeNode {
    fn new(value: u32, parent: Option<Rc<RefCell<TreeNode>>>) -> Rc<RefCell<TreeNode>> {
        Rc::new(RefCell::new(TreeNode { value, parent, children: vec![] }))
    }
}
#![allow(dead_code)]
use std::rc::Rc;
use std::cell::RefCell;
//norun
struct TreeNode {
    value: u32,
    parent: Option>>,
    children: Vec>>,
}

impl TreeNode {
    fn new(value: u32, parent: Option>>) -> Rc> {
        Rc::new(RefCell::new(TreeNode { value, parent, children: vec![] }))
    }
}
fn main() {}

Обратно към Rc

Side note

Може да си улесните малко живота с type alias:

1 2 3 4 5 6 7 8 9 10 11 12 13
type TreeNodeRef = Rc<RefCell<TreeNode>>;

struct TreeNode {
    value: u32,
    parent: Option<TreeNodeRef>,
    children: Vec<TreeNodeRef>,
}

impl TreeNode {
    fn new(value: u32, parent: Option<TreeNodeRef>) -> TreeNodeRef {
        Rc::new(RefCell::new(TreeNode { value, parent, children: vec![] }))
    }
}
#![allow(dead_code)]
use std::rc::Rc;
use std::cell::RefCell;
//norun

type TreeNodeRef = Rc>;

struct TreeNode {
    value: u32,
    parent: Option,
    children: Vec,
}

impl TreeNode {
    fn new(value: u32, parent: Option) -> TreeNodeRef {
        Rc::new(RefCell::new(TreeNode { value, parent, children: vec![] }))
    }
}
fn main() {}

Weak reference

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
fn make_tree() -> Rc<RefCell<TreeNode>> {
    let root = TreeNode::new(0, None);
    let v1 = TreeNode::new(1, Some(Rc::clone(&root)));
    let v2 = TreeNode::new(2, Some(Rc::clone(&root)));

    {
        let mut r = root.borrow_mut();
        r.children.push(v1);
        r.children.push(v2);
    }

    root
}

fn main() {
    let tree = make_tree();
    println!("{:?}", tree.borrow().value);
    mem::drop(tree);
}
0
#![allow(dead_code)]
use std::rc::Rc;
use std::cell::RefCell;
use std::mem;
#[derive(Debug)]
struct TreeNode {
value: u32,
parent: Option>>,
children: Vec>>,
}
impl TreeNode {
fn new(value: u32, parent: Option>>) -> Rc> {
Rc::new(RefCell::new(TreeNode { value, parent, children: vec![] }))
}
}
fn make_tree() -> Rc> {
    let root = TreeNode::new(0, None);
    let v1 = TreeNode::new(1, Some(Rc::clone(&root)));
    let v2 = TreeNode::new(2, Some(Rc::clone(&root)));

    {
        let mut r = root.borrow_mut();
        r.children.push(v1);
        r.children.push(v2);
    }

    root
}

fn main() {
    let tree = make_tree();
    println!("{:?}", tree.borrow().value);
    mem::drop(tree);
}

Weak reference

Weak reference

Weak reference

Weak reference

Sidenote

Weak reference

Sidenote

Weak reference

Sidenote

Weak reference

Да се върнем на проблема с дървото

Weak reference

Да се върнем на проблема с дървото

Weak reference

Да се върнем на проблема с дървото

Weak reference

Да се върнем на проблема с дървото

Weak reference

1 2 3 4 5 6 7 8 9 10 11 12
use std::mem;
use std::rc::{Rc, Weak};

fn main() {
    let rc = Rc::new(10);
    let weak = Rc::downgrade(&rc);

    println!("{:?}", Weak::upgrade(&weak)); // Option<Rc<T>>

    mem::drop(rc);
    println!("{:?}", Weak::upgrade(&weak)); // Option<Rc<T>>
}
Some(10) None
use std::mem;
use std::rc::{Rc, Weak};

fn main() {
    let rc = Rc::new(10);
    let weak = Rc::downgrade(&rc);

    println!("{:?}", Weak::upgrade(&weak)); // Option>

    mem::drop(rc);
    println!("{:?}", Weak::upgrade(&weak)); // Option>
}

Reference counting

Weak references

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
let gosho_source = "Гошо, Гошо, скочи лошо";
let shared_gosho = Rc::new(gosho_source); // shared_gosho { strong = 1, weak = 0 };

let bratcheda = Rc::clone(&shared_gosho); // shared_gosho { strong = 2, weak = 0 };
// или, shared_gosho.clone(), но първото е по-ясно

let slabichko = Rc::downgrade(&shared_gosho); // shared_gosho { strong = 2, weak = 1 };
println!("{:#?}", Weak::upgrade(&slabichko)); // => Some("Гошо, Гошо, скочи лошо")
                                              // shared_gosho { strong = 3, weak = 1 };
                                              // shared_gosho { strong = 2, weak = 1 };

std::mem::drop(bratcheda); // shared_gosho { strong = 1, weak = 1 };
std::mem::drop(shared_gosho); // shared_gosho { strong = 0, weak = 1 }; => DROP!

println!("{:#?}", Weak::upgrade(&slabichko)); // => None

Rc

1 2 3 4 5 6 7 8
// Инициализираме споделената стойност
let gosho_source = "Гошо, Гошо, скочи лошо";
let shared_gosho = Rc::new(gosho_source); // Rc<&str>

let bratcheda = Rc::clone(&shared_gosho); // Rc<&str>

let slabichko = Rc::downgrade(&shared_gosho); // Weak<&str>
println!("{:#?}", Weak::upgrade(&slabichko)); // Option<Rc<&str>>

Rc

Ето как изглежда в паметта:

Raw pointers

Ок, нека видим и как изглеждат указателите

1 2 3 4 5 6
let raw_const_ptr: *const u32 = &1_u32 as *const u32;
let raw_mut_ptr: *mut u32 = &mut 1_u32 as *mut u32;

// Coercion
let raw_const_ptr: *const u32 = &1;
let raw_mut_ptr: *mut u32 = &mut 1;
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() {
let raw_const_ptr: *const u32 = &1_u32 as *const u32;
let raw_mut_ptr: *mut u32 = &mut 1_u32 as *mut u32;

// Coercion
let raw_const_ptr: *const u32 = &1;
let raw_mut_ptr: *mut u32 = &mut 1;
}

Raw pointers

Първо и най-важно правило - указателите са safe, докато не се опитаме да четем или пишем в тях

1 2 3 4 5 6 7 8 9 10 11 12 13
let raw_const_ptr: *const u32 = &1;
let raw_mut_ptr: *mut u32 = &mut 1;

unsafe {
    println!("{}", *raw_const_ptr);
}

// За разлика от референциите, указателите
// могат да са null или dangling pointers
unsafe {
    *raw_mut_ptr += 1;
    println!("{}", *raw_mut_ptr);
}
1 2
fn main() {
let raw_const_ptr: *const u32 = &1;
let raw_mut_ptr: *mut u32 = &mut 1;

unsafe {
    println!("{}", *raw_const_ptr);
}

// За разлика от референциите, указателите
// могат да са null или dangling pointers
unsafe {
    *raw_mut_ptr += 1;
    println!("{}", *raw_mut_ptr);
}
}

Raw pointers

Нямат Deref и DerefMut. Тук компилатора си има само вградения оператор за това.

1
(&1 as *const u32).deref();
error[E0599]: no method named `deref` found for raw pointer `*const u32` in the current scope --> src/bin/main_a8c848ea06ad2b67e0c50e5c33a165c61f58c8ce.rs:2:20 | 2 | (&1 as *const u32).deref(); | ^^^^^ method not found in `*const u32` For more information about this error, try `rustc --explain E0599`. error: could not compile `rust` (bin "main_a8c848ea06ad2b67e0c50e5c33a165c61f58c8ce") due to 1 previous error
fn main() {
(&1 as *const u32).deref();
}

Raw pointers

Някои полезни методи

Raw pointers

Някои полезни методи

Raw pointers

Някои полезни методи

Raw pointers

Някои полезни методи

Raw pointers

Някои полезни методи

Въпроси