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

07 ноември 2019

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

Преговор

1 2 3 4 5 6
// Резултатът е свързан и с двата аргумента
// (може кой да е от двата да е резултат)
fn longest<'a>(first: &'a str, second: &'a str) -> &'a str

// Резултатът е свързан с първия аргумент
fn find_substring<'a>(text: &'a str, query: &str) -> Option<&'a str>
#![allow(unused_variables)]
#![allow(dead_code)]
// Резултатът е свързан и с двата аргумента
// (може кой да е от двата да е резултат)
fn longest<'a>(first: &'a str, second: &'a str) -> &'a str
{ unimplemented!() }

// Резултатът е свързан с първия аргумент
fn find_substring<'a>(text: &'a str, query: &str) -> Option<&'a str>
{ unimplemented!() }
fn main() {}

Преговор

1 2 3 4 5 6 7 8 9
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
#![allow(dead_code)]
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
fn main() {}

Smart pointers

?Sized ???

?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

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

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

Box

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

    println!("{}", x + y);
}
error[E0369]: binary operation `+` cannot be applied to type `std::boxed::Box<{integer}>` --> src/bin/main_81c11fbe09e1559f40de38f65b4627444769a9b5.rs:5:22 | 5 | println!("{}", x + y); | - ^ - std::boxed::Box<{integer}> | | | std::boxed::Box<{integer}> | = note: an implementation of `std::ops::Add` might be missing for `std::boxed::Box<{integer}>`
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: not magic)

Box

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

Box

Linked list

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

Box

Linked list

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 indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable error[E0391]: cycle detected when processing `List` --> src/bin/main_8ba92f92a6708ff40e65364f4344a56ecfa46195.rs:2:1 | 2 | enum List { | ^^^^^^^^^ | = note: ...which again requires processing `List`, completing the cycle = note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing, def_id: None }, value: List } }`
#[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

Linked list

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

Linked list

Можем ли вместо 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

Linked list

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#[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> {
    Cons(x, &Cons(y, &Nil))
}

fn main() {
    println!("{:?}", return_list(1, 2));
}
error[E0515]: cannot return value referencing temporary value --> src/bin/main_567d155504aa95d3cfd7da212db462492777c6d6.rs:10:5 | 10 | Cons(x, &Cons(y, &Nil)) | ^^^^^^^^^-------------^ | | | | | temporary value created here | returns a value referencing data owned by the current function
#[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> {
    Cons(x, &Cons(y, &Nil))
}

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

Box

Linked list

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

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

use List::{Cons, Nil};

fn return_list(x: i32, y: i32) -> List {
    Cons(x, Box::new(Cons(y, Box::new(Nil))))
}

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 {
    Cons(x, Box::new(Cons(y, Box::new(Nil))))
}

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

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 a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
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
fn vec_of_things() -> Vec<Box<dyn Display>> {
    let x = 123;
    vec![Box::new(x), Box::new(3.14), Box::new("foobar")]
}
#![allow(dead_code)]
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() {}

Box

Trait objects

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

Това рядко ще е проблем откъм peformance (освен ако не пишете eclipse, I guess: https://twitter.com/pcwalton/status/1169004435856592901)

Nightly Rust

Nightly Rust

Nightly Rust

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

Nightly features

1 2 3 4 5 6 7 8 9 10 11 12
fn one_step_eval(t: Term) -> Result<Term, String> {
    match t {
        Term::If(t1, t2, t3) => {
            match *t1 { // След малко ще си говорим за Deref, спокойно!
                Term::True => Ok(*t2),
                Term::False => Ok(*t3),
                _ => Ok(Term::If(Box::new(one_step_eval(*t1)?), t2, t3)),
            }
        },
        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}
#![allow(dead_code)]
#[derive(Clone, Debug, PartialEq)]
pub enum Term {
True,
False,
If(Box, Box, Box),
Value
}
fn one_step_eval(t: Term) -> Result {
    match t {
        Term::If(t1, t2, t3) => {
            match *t1 { // След малко ще си говорим за Deref, спокойно!
                Term::True => Ok(*t2),
                Term::False => Ok(*t3),
                _ => Ok(Term::If(Box::new(one_step_eval(*t1)?), t2, t3)),
            }
        },
        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}
fn main() {}

Box

Nightly features

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

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

Box

Nightly features

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

1
#![feature(box_patterns)]

Deref

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

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

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

Deref

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

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

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

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

Deref не връща T, защото това би ни дало 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 my_favorite_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], *my_favorite_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 my_favorite_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], *my_favorite_song);
}

Deref

1 2 3 4 5 6 7 8 9
*my_favorite_song

*(my_favorite_song.deref())

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

*(&my_favorite_song.audio)

Deref

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

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

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

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

compress_mp3(my_favorite_song.audio.as_slice())

Deref

deref coercion

Deref

deref coercion

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

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

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

Deref

deref coercion

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

//
//
//
//
compress_mp3(&my_favorite_song.deref().deref())

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

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

deref coercion

Deref

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

*a = 5;

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

Reference counting

Reference counting

Reference counting

Cow

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

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 11 12 13
use std::cell::RefCell;

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

    {
        let mut r = cell.borrow_mut(); // -> RefMut<String>
        r.push_str("bar");
    }

    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

    {
        let mut r = cell.borrow_mut(); // -> RefMut
        r.push_str("bar");
    }

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

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

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

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

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

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

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

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/libcore/result.rs:1084:5 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

Обратно към Rc

Weak reference

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

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

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

impl TreeNode {
    fn new(index: u32, parent: Option>>) -> Rc> {
        Rc::new(RefCell::new(TreeNode { index, 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 {
    index: u32,
    parent: Option<TreeNodeRef>,
    children: Vec<TreeNodeRef>,
}

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

type TreeNodeRef = Rc>;

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

impl TreeNode {
    fn new(index: u32, parent: Option) -> TreeNodeRef {
        Rc::new(RefCell::new(TreeNode { index, 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().index);
    mem::drop(tree);
}
0
#![allow(dead_code)]
use std::rc::Rc;
use std::cell::RefCell;
use std::mem;
#[derive(Debug)]
struct TreeNode {
index: u32,
parent: Option>>,
children: Vec>>,
}
impl TreeNode {
fn new(index: u32, parent: Option>>) -> Rc> {
Rc::new(RefCell::new(TreeNode { index, 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().index);
    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 type `*const u32` in the current scope --> src/bin/main_a8c848ea06ad2b67e0c50e5c33a165c61f58c8ce.rs:2:20 | 2 | (&1 as *const u32).deref(); | ^^^^^ | = 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
fn main() {
(&1 as *const u32).deref();
}

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Въпроси