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

8 ноември 2018

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

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

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

Преговор

Преговор

Преговор

Преговор

Преговор

Smart pointers

?Sized ???

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

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);
}
Любов, любов, варен картоф,
разрежеш го, а той суров.

Box

Reference + ownership!

Box

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

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

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

(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/main_4.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

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

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[E0597]: `x` does not live long enough
 --> /src/main_6.rs:4:11
  |
4 |     vec![&x, &3.14, &"foobar"]
  |           ^ borrowed value does not live long enough
5 | }
  | - borrowed value only lives until here
  |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 2:18...
 --> /src/main_6.rs:2:18
  |
2 | fn vec_of_things<'a>() -> Vec<&'a dyn Display> {
  |                  ^^

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

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)

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
}

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

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

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

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

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

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

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

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

Reference counting

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

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

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 immutable borrowed content
 --> /src/main_19.rs:5:1
  |
5 | *a = 5;
  | ^^^^^^ cannot borrow as mutable

warning: variable does not need to be mutable
 --> /src/main_19.rs:3:5
  |
3 | let mut a = Rc::new(3);
  |     ----^
  |     |
  |     help: remove this `mut`
  |
  = note: #[warn(unused_mut)] on by default

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

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

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

Internal mutability

Cell

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

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

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

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

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', libcore/result.rs:1009:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

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

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

Обратно към 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![] }))
    }
}

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

Weak reference

Weak reference

Weak reference

Weak reference

Sidenote

Weak reference

Sidenote

Weak reference

Sidenote

Weak reference

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

Weak reference

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

Weak reference

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

Weak reference

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

Weak reference

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

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

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

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

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;

Raw pointers

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

1 2 3 4 5 6 7 8 9 10 11 12
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

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/main_33.rs:2:20
  |
2 | (&1 as *const u32).deref();
  |                    ^^^^^

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Raw pointers

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

Въпроси