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

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

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

15 ноември 2017

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

Второ домашно! Отложено до понеделник!

Преговор

Smart pointers

Box

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

5

Box

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/main.rs:5:23
  |
5 |     println!("{:#?}", x + y);
  |                       ^^^^^
  |
  = note: an implementation of `std::ops::Add` might be missing for `std::boxed::Box<{integer}>`

Box

fn main() {
    let x = Box::new(3);
    let y = Box::new(5);

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

Box

use std::mem::size_of_val;

fn main() {
    let v = vec!["baba", "dyado"];
    println!("v       = {:?}", v);
    println!("size(v) = {:?}", size_of_val(&v));

    println!("-----------------------------");

    let bv = Box::new(vec!["baba", "dyado"]);
    println!("bv        = {:?}", bv);
    println!("size(bv)  = {:?}", size_of_val(&bv));
    println!("size(*bv) = {:?}", size_of_val(&*bv));
}

Box

v       = ["baba", "dyado"]
size(v) = 24
-----------------------------
bv        = ["baba", "dyado"]
size(bv)  = 8
size(*bv) = 24

Box

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

Box

Linked 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

error[E0072]: recursive type `List` has infinite size
--> src/main.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

Box

Linked list

#[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)))

Box

Trait objects

fn vec_of_things<'a>() -> Vec<&'a Display> {
    let x = 123;
    vec![&x, &3.14, &"foobar"]                              // грешка при компилация
}

fn vec_of_things() -> Vec<Box<Display>> {
    let x = 123;
    vec![Box::new(x), Box::new(3.14), Box::new("foobar")]   // ok
}

Box

Trait objects

Box<Error> - ако ни мързи да правим error handling

fn get_x() -> Result<i32, io::Error> { ... }
fn get_y() -> Result<i32, fmt::Error> { ... }

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

Nightly Rust

Box

Nightly features

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

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', трябва да го оповестите така в началото на програмата си:

#![feature(box_syntax)]

enum Fruit {
  Apple,
  Orange,
  Tomato,
  Cherry
}

fn main() {
  let apple_in_a_box = box Fruit::Apple;
}

Box

Nightly features

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

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    True,
    False,
    If(Box<Term>, Box<Term>, Box<Term>),
    Value
}

Box

Nightly features

fn one_step_eval(t: Term) -> Result<Term, String> {
    match t {
        If(t1, t2, t3) => {
            match *t1 { // След малко ще си говорим за Deref, спокойно!
                True => Ok(*t2),
                False => Ok(*t3),
                _ => Ok(If(Box::new(one_step_eval(*t1)?), t2, t3)),
            }
        },
        any => Err(format!("Term can't be evaluated : {:?}", any))
    }
}

Box

Nightly features

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

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

Box

Nightly features

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

#![feature(box_patterns)]

Deref

let mut x = 5;
{
    let y = &mut x;

    *y += 1
}

Deref

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

Deref

?Sized ???

Deref

?Sized ???

Deref

?Sized ???

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

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

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

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

Deref

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

Deref

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

*my_favorite_song

*(my_favorite_song.deref())

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

*(&my_favorite_song.audio)

Deref

deref coercion

fn compress_mp3(audio: &[u8]) -> Vec<u8> {
    // ...
}

compress_mp3(my_favorite_song.audio.as_slice()

Deref

deref coercion

&[u32; 5]&[u32]

&Vec<u32>&[u32]

&String&str

Deref

deref coercion

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)

Deref

deref coercion

Deref

Reference counting

use std::rc::Rc;

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

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

Reference counting

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

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

Reference counting

let mut a = Rc::new(3);

*a = 5;

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

Reference counting

error[E0594]: cannot assign to immutable borrowed content
--> src/main.rs:6:5
  |
6 |     *a = 5;
  |     ^^^^^^ cannot borrow as mutable 

Reference counting

Cow

use std::rc::Rc;

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

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

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

Cow

use std::rc::Rc;

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

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

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

Cow

Cow

Copy on Write

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

Internal mutability

Internal mutability

use std::cell::Cell;

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

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

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

Internal mutability

Cell

Internal mutability

use std::cell::RefCell;

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

    {
        let mut r = cell.borrow_mut();
        r.push_str("bar");
    }

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

Internal mutability

RefCell

Internal mutability

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

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 

Weak reference

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![] }))
    }
}

Weak reference

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
}

let tree = make_tree();
mem::drop(tree);          // ?

Weak reference

Weak reference

Sidenote

Weak reference

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

Weak reference

use std::mem;
use std::rc::{Rc, Weak};

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

    println!("{:?}", weak.upgrade());

    mem::drop(rc);
    println!("{:?}", weak.upgrade());
}
Some(10)
None 

Container cheat sheet

Въпроси