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

09 ноември 2021

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Малък тест

Малък тест

Малък тест

Малък тест

Малък тест

Малък тест

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: 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

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>), | ++++ + error[E0391]: cycle detected when computing drop-check constraints for `List` --> src/bin/main_8ba92f92a6708ff40e65364f4344a56ecfa46195.rs:2:1 | 2 | enum List { | ^^^^^^^^^ | = note: ...which immediately requires computing drop-check constraints for `List` again = note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: List } }` Some errors have detailed explanations: E0072, E0391. For more information about an error, try `rustc --explain E0072`. error: could not compile `rust` 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

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
#[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 For more information about this error, try `rustc --explain E0515`. error: could not compile `rust` due to previous error
#[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

Никакъв проблем ако ползваме 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 (преговор)

Ако имаме 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 (преговор)

Box

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

Box

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

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

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 (освен ако не пишете eclipse, I guess: https://twitter.com/pcwalton/status/1169004435856592901)

Box

1 2 3 4 5 6 7 8 9 10
fn curry_static(a: u32) -> impl Fn(u32) -> u32 {
    move |b| a + b
}

fn curry_dynamic(a: u32) -> Box<dyn Fn(u32) -> u32> {
    Box::new(move |b| a + b)
}

println!("{}", curry_static(1)(2));
println!("{}", curry_dynamic(1)(2));
3 3
fn curry_static(a: u32) -> impl Fn(u32) -> u32 {
    move |b| a + b
}

fn curry_dynamic(a: u32) -> Box u32> {
    Box::new(move |b| a + b)
}

fn main() {
println!("{}", curry_static(1)(2));
println!("{}", curry_dynamic(1)(2));
}

В този конкретен случай, и двете са валидни начини да постигнем решението. Но impl се свежда до един конкретен тип at compile-time.

Box

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
struct UiElement {
    title: String,
    on_click: Box<dyn Fn()>,
}

fn main() {
    let mut clicker = UiElement {
        title: String::from("Yoda Clicker"),
        on_click: Box::new(|| println!("Clicked, I have been!")),
    };

    (clicker.on_click)();
    clicker.on_click = Box::new(|| println!("Click, or click not -- there is no hover"));
    (clicker.on_click)();
}
Clicked, I have been! Click, or click not -- there is no hover
struct UiElement {
    title: String,
    on_click: Box,
}

fn main() {
    let mut clicker = UiElement {
        title: String::from("Yoda Clicker"),
        on_click: Box::new(|| println!("Clicked, I have been!")),
    };

    (clicker.on_click)();
    clicker.on_click = Box::new(|| println!("Click, or click not -- there is no hover"));
    (clicker.on_click)();
}

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

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

Използвайки 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

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

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

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

Това работи и за 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
*appropriately_named_song

*(appropriately_named_song.deref())

fn deref(&self) -> &Vec<u8> {
    &self.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

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(appropriately_named_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(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 8 9 10 11 12
fn compress_mp3(audio: &[u8]) -> Vec<u8> {
    // ...
}

//
//
//
//
compress_mp3(&appropriately_named_song.deref().deref())

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

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

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

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/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

Обратно към 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

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

Въпроси