12. Умни указатели, втори дубъл

12. Умни указатели, втори дубъл

12. Умни указатели, втори дубъл

17 ноември 2017

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

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

Разширен преговор

Умни указатели, умно обяснени?

&T

Най-глупавия указател, но важен за целите на сравнението

&T

&T

let potato = String::from("
    Любов, любов, варен картоф,
    разрежеш го, а той суров.
");

let lines = potato.
    trim().
    lines().
    map(|l| l.trim());

for line in lines {
    println!("{}", line);
}

Box

Също толкова глупав, но има ownership

Box

Rc

Умничък

Rc

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

// Инициализираме споделената стойност
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

(Holy) Cow

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

    {
        let mut_ref_a = Rc::make_mut(&mut a); // &mut i32
        // създава се чисто нова "вътрешна" стойност, към която сочи a,
        // с извикване на `clone()`
        *mut_ref_a = 5;
    }

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

Rc

Cell, RefCell

Cell

use std::cell::Cell;

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

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

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

RefCell

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 

Cell, RefCell

Deref

Айде пак звездички и амперсанди.

Deref

Deref и DerefMut правят две неща:

Deref

Deref не връща T, защото това би ни дало ownership над стойността и в случая на Box, това ще премести стойноста и ще направи инстанцията неизползваема.

pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target; // => винаги връща reference!
}

Deref

fn is_ref<T: ?Sized + Any>(_s: &T) -> bool {
    TypeId::of::<&i32>() == TypeId::of::<&T>()
}

println!("{}", is_ref(&1));
println!("{}", is_ref((&1).deref()));
println!("{}", is_ref(Box::new(1).deref()));

// println!("{}", is_ref(*(&1)));
// println!("{}", is_ref(*Box::new(1)));

Deref

Като говорим за присвояване

let a = &mut 1;

a = 2 // Това не става, не може да присвоим T на &mut T
a.deref_mut() = 2; // Същото
a.deref_mut() = &mut 2; // Емиииии.. не

*a = 2; // Това ще е то

let mut a = a; // deref_mut взима &mut self

*(a.deref_mut()) = 2; // Това е идентитет след което го дереференцираме

Deref

Върху типове указатели (&T, &mut T, *const T, *mut T) оператора * дереференцира указателя -- дава ни истинската стойност, и ни позволява да я манипулираме

Това поведение е дефинирано от компилатора и не може да се променя

let x: &T;
let y = *x;       // T

let x: &&T;
let y = *x;       // &T
let y = **x;      // T

Deref

Върху типове, които не са указатели и за които е дефиниран Deref се извиква функцията deref по следния начин

impl Deref for T {
    type Target = U;

    fn deref(&self) -> &U { ... }
}

let x: T;
let y = *x;

// това се превежда до
let y = *Deref::deref(&x); // => *&U == U

Deref

Пример

let x: T;
let y = x.deref();        // &U
let y = *(x.deref());     // U
let y = *x;               // U

let x: &T;
let y = *x;               // T
let y = **x;              // U

Deref

DerefMut

Прилага се по същият начин като Deref

Използва се за mutable операции, като *x = val;

trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Deref

Може да си ги мислите като lvalue и rvalue от C++

let a = &1;

let b = *a;     // ок
*a = 2;         // комп. грешка, не е mutable reference


let a = &mut 1;

let b = *a;     // ок
*a = 2;         // ok

Deref

Същото нещо работи и ако използваме Deref

let a = Box::new(1);

let b = *a;     // ок, deref от Box<i32> до i32
*a = 2;         // комп. грешка, не държи box-а mutably


let mut a = Box::new(1);

let b = *a;     // ок, deref от Box<i32> до i32
*a = 2;         // ок, deref_mut от Box<i32> до i32

Deref

Същият код, разписан

let a = Box::new(1);

let b = *(Deref::deref(&a));
*(DerefMut::deref_mut(&mut a)) = 2;   // cannot borrow as mutable


let mut a = Box::new(1);

let b = *(Deref::deref(&a));
*(DerefMut::deref_mut(&mut a)) = 2;

Deref

Това е overload само за custom types и по-точно типове, които имат поведение на указатели.

Raw pointers

Ок, нека видим и как изглеждат указателите

let raw_const_ptr: *const u32 = &1 as *const u32;
let raw_mut_ptr: *mut u32 = &mut 1 as *mut u32;

// Coercion
let raw_const_ptr: *const u32 = &1;
let raw_mut_ptr: *mut u32 = &mut 1;

Raw pointers

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

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

Raw pointers

Нямат Deref и DerefMut. Тук компилатора си има само вградения оператор за това.

// no method named `deref` found for type `*const u32` in the current scope
(&1 as *const u32).deref();

Raw pointers

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

Coercions

Types

"Coercion" се случва за тези типове:

Coercions

Свеждане

https://doc.rust-lang.org/beta/reference/type-coercions.html

Прочетете си това, по-подробно е

Coercions

Свеждане

Това не е inference! Това е имплицитно определяне на тип, когато знаем експлицитно към какъв тип се стремим.

Coercions

Пример

Свеждания които сме срещали досега

fn uses_num(num: &i32) { ... }

let x = 10;
uses_num(&x);
uses_num(&&&&x);                          // &&&&i32 -> &i32

Coercions

Deref Coercion

Имплицитно добавя извиквания на deref / deref_mut

let boxed_num = Box::new(12);

let x = &boxed_num;                       // x: &Box<i32>
let x: &i32 = &boxed_num;                 // &Box<i32> -> &i32
let x: &i32 = Deref::deref(&boxed_num);   // expanded

Coercions

Deref Coercion

Имплицитно добавя извиквания на deref / deref_mut

fn uses_num(num: &i32) { ... }

let boxed_num = Box::new(12);

let x = &boxed_num;                   // x: &Box<i32>, inference
uses_num(x);                          // &Box<i32> -> &i32
uses_num(Deref::deref(x));            // expanded

uses_num(&boxed_num);                 // &Box<i32> -> &i32
uses_num(Deref::deref(&boxed_num));   // expanded

Coercions

Deref Coercion

Свеждането е транзитивно

fn print_bytes(bytes: &[u8]) { ... }

// използваме типа Mp3 от миналата лекция,
// който имаше дефиниран Deref до Vec<u8>
let song = Mp3 { ... };

print_bytes(&song);                   // &Mp3 -> &Vec<u8> -> &[u8]
print_bytes(song.deref().deref());    // expanded

Coercions

Deref Coercion

let mut song = Mp3 { ... };

song.push(42);
Vec::push(song.deref_mut(), 42);      // expanded

song.first();
<[u8]>::first(song.deref().deref());  // expanded

HashMap

Нека да освежим малко, че все пак е петък

HashMap

struct HashMap<K, V> { /* fields omitted */ }

HashMap

Изисква ключа да имплементира Hash + Eq

impl<K: Hash + Eq, V> HashMap<K, V> { /* ... */}

HashMap

Hash

#[derive(Hash, PartialEq, Eq)]
struct MyStruct { /* ... */ }

HashMap

Добавяне на елементи

// Типът ще бъде <&str, &str>
let mut book_reviews = HashMap::new();

book_reviews.insert("Adventures of Huckleberry Finn",    "My favorite book.");
book_reviews.insert("Grimms' Fairy Tales",               "Masterpiece.");
book_reviews.insert("Pride and Prejudice",               "Very enjoyable.");
book_reviews.insert("The Adventures of Sherlock Holmes", "Eye lyked it alot.");

HashMap

if !book_reviews.contains_key("Les Misérables") {
    println!("We've got {} reviews, but Les Misérables ain't one.", book_reviews.len());
}

HashMap

Премахване на елементи

// Прекалени много правописни грешки, нека го махнем.
book_reviews.remove("The Adventures of Sherlock Holmes");

HashMap

Взимане на стойност по ключ

let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"];
for book in &to_find {
    match book_reviews.get(book) {
        Some(review) => println!("{}: {}", book, review),
        None => println!("{} is unreviewed.", book)
    }
}

HashMap

Итериране

// Итериране по ключ-стойност двойките
for (book, review) in &book_reviews {
    println!(r#"{}: "{}""#, book, review);
}

HashMap

Entry

use std::collections::HashMap;

let mut map: HashMap<&str, u32> = HashMap::new();
// Добавя poneyland => 22, защото няма такова
map.entry("poneyland").or_insert(12);

assert_eq!(map["poneyland"], 12);

*map.entry("poneyland").or_insert(15) += 10;

println!("poneyland => {}", map["poneyland"]); // ??

Poneyland => 22

HashMap

Borrow

let mut book_reviews = HashMap::new();

book_reviews.insert("Adventures of Huckleberry Finn",    "My favorite book.");
book_reviews.insert("Grimms' Fairy Tales",               "Masterpiece.");
book_reviews.insert("Pride and Prejudice",               "Very enjoyable.");
book_reviews.insert("The Adventures of Sherlock Holmes", "Eye lyked it alot.");

HashMap

Borrow

Така изглежда get. Подобно е за Index

pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq

HashMap

Borrow

Това ни позволява да правим неща като

book_reviews.get("Adventures of Huckleberry Finn");
book_reviews.get(String::from("Grimms' Fairy Tales"));

HashMap

Borrow

Това е заради Borrow trait-a

trait Borrow<Borrowed: ?Sized> {
    fn borrow(&self) -> &Borrowed;
}

HashMap

Borrow

use std::borrow::Borrow;

fn check<T: Borrow<str>>(s: T) {
    assert_eq!("Hello", s.borrow());
}

let s = String::from("Hello");
check(s);

let s = "Hello";
check(s);

AsRef

Има и нещо подобно

trait AsRef<T: ?Sized> {
    fn as_ref(&self) -> &T;
}

trait Borrow<Borrowed: ?Sized> {
    fn borrow(&self) -> &Borrowed;
}

AsRef

Защо?

AsRef

Семантично са различни

AsRef

let s = String::from("hi");
let r: &String = &s;
let r: &[u8] = s.as_ref();
let r: &str = s.as_ref();

Въпроси