struct Square { width: f32 } fn main() { let x: u64 = 0; let y = Square { width: 1.0 }; let mut z: String = "Hello".to_string(); z.push_str(", world!"); }
struct Square { width: f32 } fn main() { let x: u64 = 0; let y = Square { width: 1.0 }; let mut z: String = "Hello".to_string(); z.push_str(", world!"); println!("x @ {:p}", &x); println!("y @ {:p}", &y); println!("z @ {:p}", &z); println!("z @ {:p}", z.as_str()); }
On three levels:
Box
, Rc
, Vec
, etcBox<T>
in Rust, is a handle to a unique, owned, heap-allocated value of type T
Because Box<T>
:
Deref<T>
and DerefMut<T>
The Deref
and DerefMut
trait implementations let us use a Box quite naturally:
fn main() { let x: Box<f64> = Box::new(1.0_f64); let y: f64 = x.sin() * 2.0; let z: &f64 = &x; println!("x={x}, y={y}, z={z}"); }
Vec<T>
) existBox<T>
is cheap, because only the pointer moves, not the contentsfn make_stuff(want_integer: bool) -> Box<dyn std::fmt::Debug> { if want_integer { Box::new(42_i32) } else { Box::new("Hello".to_string()) } } fn main() { println!("make_stuff(true): {:?}", make_stuff(true)); println!("make_stuff(false): {:?}", make_stuff(false)); }
What if I want my Box to have multiple owners? And for the memory to be freed when both of the owners have finished with it?
We have the reference counted Rc<T>
type for that!
Rc<T>
use std::rc::Rc; struct Point { x: i32, y: i32 } fn main() { let first_handle = Rc::new(Point { x: 1, y: 1}); let second_handle = first_handle.clone(); let third_handle = second_handle.clone(); }
Rc
type is a handle to reference-counted heap allocationclone()
the count goes up by oneWeak
version which will not keep the allocation alive - to break cyclesRc
cannot be sent into a thread (or through any API that requires the type to be Send
).Rc
is really fast!Arc
if you need it.NB: Rc
allows sharing, but not mutability...
use std::rc::{Rc, Weak}; struct Dog { name: String, owner: Weak<Human> } struct Human { name: String, pet_dogs: Vec<Dog> } fn main() { let mut sam = Rc::new( Human { name: "Sam".to_string(), pet_dogs: Vec::new() } ); let rover = Dog { name: "Rover".to_string(), owner: Rc::downgrade(&sam) }; // This is not allowed, because `sam` is actually immutable // sam.pet_dogs.push(rover); }
We have more on this later...
use std::rc::{Rc, Weak}; use std::cell::RefCell; struct Dog { name: String, owner: Weak<RefCell<Human>> } struct Human { name: String, pet_dogs: Vec<Dog> } fn main() { let mut sam = Rc::new(RefCell::new( Human { name: "Sam".to_string(), pet_dogs: Vec::new() } )); let rover = Dog { name: "Rover".to_string(), owner: Rc::downgrade(&sam) }; // This is now allowed because `RefCell::borrow_mut` does a run-time borrow check sam.borrow_mut().pet_dogs.push(rover); }
Why is this function less than ideal?
/// Replaces all the ` ` characters with `_` fn replace_spaces(input: &str) -> String { todo!() } fn main() { println!("{}", replace_spaces("Hello, world!")); println!("{}", replace_spaces("Hello!")); }
Rust has the Cow
type to handle this.
/// Replaces all the ` ` characters with `_` fn replace_spaces(input: &str) -> std::borrow::Cow<str> { todo!() } fn main() { println!("{}", replace_spaces("Hello, world!")); println!("{}", replace_spaces("Hello!")); }