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

08 ноември 2022

Преговор

Преговор

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}> For more information about this error, try `rustc --explain E0369`. error: could not compile `rust` due to 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 { | ^^^^^^^^^ recursive type has infinite size 3 | Nil, 4 | Cons(i32, List), | ---- recursive without indirection | help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable | 4 | Cons(i32, Box<List>), | ++++ + For more information about this error, try `rustc --explain E0072`. error: could not compile `rust` due to previous error
#[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` 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` due to 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.

Nightly Rust

Nightly Rust

Box

Nightly features

Има специален keyword : box за създаване на Box smart pointer-и

1 2 3 4 5 6
let x = Box::new(5);
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

// Може да се напише така:
let x = box 5;
let list = Cons(1, box Cons(2, box Cons(3, box Nil)));

Box

Nightly features

За да може да използвате този 'feature', трябва да го оповестите така в началото на програмата си:

1 2 3 4 5 6 7 8 9
#![feature(box_syntax)]

struct Heart {
    owner: &'static str,
}

fn main() {
  let heart_shaped_box = box Heart { owner: "Kurt" };
}

Box

Nightly features

Ключовата дума box е мнооого полезна при pattern matching! Пример:

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<Term> не може да се pattern-match-не по компоненти -- вътрешността му е private.

Box

Nightly features

Използвайки feature(box_patterns), можем да pattern-match-ваме Box<Term>:

1 2 3 4 5 6 7 8 9 10 11
#![feature(box_syntax)]
#![feature(box_patterns)]

fn one_step_eval(t: Term) -> Result<Term, String> {
    match t {
        Term::If(box Term::True, t2, _) => Ok(*t2),
        Term::If(box Term::False, _, t3) => Ok(*t3),
        Term::If(t1, t2, t3) => Ok(Term::If(box one_step_eval(*t1)?, t2, t3)),
        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}

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` due to previous error
use std::rc::Rc;
fn main() {
let mut a = Rc::new(3);

*a = 5;

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

Reference counting

Reference counting

Reference counting

Copy-on-write

Cow

Copy on Write

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

Cow

Copy on Write

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

Cow

Copy on Write

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

Cow

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);
}

Cow

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);
}

Друг вариант: 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 'already borrowed: BorrowMutError', src/bin/main_45bb39151537827e2a223fcdd3995ba26df346ef.rs:9:27 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

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` | = note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref = note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior For more information about this error, try `rustc --explain E0599`. error: could not compile `rust` due to previous error
fn main() {
(&1 as *const u32).deref();
}

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Въпроси