Второ домашно! Отложено до понеделник!
Умни указатели, умно обяснени?
Най-глупавия указател, но важен за целите на сравнението
unsafe
)
let potato = String::from("
Любов, любов, варен картоф,
разрежеш го, а той суров.
");
let lines = potato.
trim().
lines().
map(|l| l.trim());
for line in lines {
println!("{}", line);
}
Също толкова глупав, но има ownership
String
~~ Box<str>
Vec<T>
~~ Box<[T]>
Умничък
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
// Инициализираме споделената стойност
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>>
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
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::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
Айде пак звездички и амперсанди.
Deref
и DerefMut
правят две неща:
deref/deref_mut
*
, ако няма
*
не е deref
Deref не връща T, защото това би ни дало ownership над стойността и в случая на Box
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target; // => винаги връща reference!
}
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)));
Като говорим за присвояване
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; // Това е идентитет след което го дереференцираме
Върху типове указатели (&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
по следния начин
impl Deref for T {
type Target = U;
fn deref(&self) -> &U { ... }
}
let x: T;
let y = *x;
// това се превежда до
let y = *Deref::deref(&x); // => *&U == U
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
Използва се за mutable операции, като *x = val;
trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
Може да си ги мислите като lvalue и rvalue от C++
let a = &1;
let b = *a; // ок
*a = 2; // комп. грешка, не е mutable reference
let a = &mut 1;
let b = *a; // ок
*a = 2; // ok
Същото нещо работи и ако използваме 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
Същият код, разписан
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;
Това е overload само за custom types и по-точно типове, които имат поведение на указатели.
Ок, нека видим и как изглеждат указателите
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;
Първо и най-важно правило - указателите са 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;
}
Нямат Deref и DerefMut. Тук компилатора си има само вградения оператор за това.
// no method named `deref` found for type `*const u32` in the current scope
(&1 as *const u32).deref();
is_null(self) -> bool
unsafe fn as_ref<'a>(self) -> Option<&'a T>
unsafe fn offset(self, count: isize) -> *const T
"Coercion" се случва за тези типове:
&mut T
to &T
*mut T
to *const T
&T
to *const T
&mut T
to *mut T
&T
to &U
if T
implements Deref<Target = U>
&mut T
to &mut U
if T
implements DerefMut<Target = U>
https://doc.rust-lang.org/beta/reference/type-coercions.html
Прочетете си това, по-подробно е
Това не е inference! Това е имплицитно определяне на тип, когато знаем експлицитно към какъв тип се стремим.
Свеждания които сме срещали досега
fn uses_num(num: &i32) { ... }
let x = 10;
uses_num(&x);
uses_num(&&&&x); // &&&&i32 -> &i32
Имплицитно добавя извиквания на 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
Имплицитно добавя извиквания на 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
Свеждането е транзитивно
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
T
до U
позволява извикване на методи дефинирани за U
Deref
позволява методи взимащи &self
DerefMut
позволява методи взимащи &mut self
let mut song = Mp3 { ... };
song.push(42);
Vec::push(song.deref_mut(), 42); // expanded
song.first();
<[u8]>::first(song.deref().deref()); // expanded
Нека да освежим малко, че все пак е петък
struct HashMap<K, V> { /* fields omitted */ }
Vec
от към методи
Map
-ове
Изисква ключа да имплементира Hash + Eq
impl<K: Hash + Eq, V> HashMap<K, V> { /* ... */}
#[derive(Hash, PartialEq, Eq)]
struct MyStruct { /* ... */ }
// Типът ще бъде <&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.");
if !book_reviews.contains_key("Les Misérables") {
println!("We've got {} reviews, but Les Misérables ain't one.", book_reviews.len());
}
// Прекалени много правописни грешки, нека го махнем.
book_reviews.remove("The Adventures of Sherlock Holmes");
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)
}
}
// Итериране по ключ-стойност двойките
for (book, review) in &book_reviews {
println!(r#"{}: "{}""#, book, review);
}
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
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.");
Така изглежда get
. Подобно е за Index
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq
Това ни позволява да правим неща като
book_reviews.get("Adventures of Huckleberry Finn");
book_reviews.get(String::from("Grimms' Fairy Tales"));
Това е заради Borrow
trait-a
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
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);
Има и нещо подобно
trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
Защо?
Семантично са различни
let s = String::from("hi");
let r: &String = &s;
let r: &[u8] = s.as_ref();
let r: &str = s.as_ref();