536 lines
13 KiB
Text
536 lines
13 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Smart Pointers"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## `Box<T>`\n",
|
|
"\n",
|
|
"* A smart pointer that allocates data on the heap.\n",
|
|
"* Provides ownership for the heap data.\n",
|
|
"* Dereferences to the data it points to.\n",
|
|
"* Useful for recursive data types and large data on the heap."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Storing Data on Heap"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"5\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"fn main() {\n",
|
|
" let on_heap = Box::new(5);\n",
|
|
" println!(\"{}\", on_heap);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Recursive Data Structures"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"enum List {\n",
|
|
" Cons(i32, Box<List>), // Box<List> allows for recursive data structures\n",
|
|
" Nil,\n",
|
|
"}\n",
|
|
"\n",
|
|
"use List::{Cons, Nil};\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Dereferencing"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"fn main() {\n",
|
|
" let x = 5;\n",
|
|
" let y = &x;\n",
|
|
" let z = Box::new(x);\n",
|
|
"\n",
|
|
" assert!(5 == x);\n",
|
|
" assert!(5 == *y);\n",
|
|
" assert!(5 == *z);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### How it works"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Creating MyBox with some data!\n",
|
|
"Dropping MyBox with some data!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"struct MyBox<T>(T);\n",
|
|
"\n",
|
|
"impl<T> MyBox<T> {\n",
|
|
" fn new(x: T) -> MyBox<T> {\n",
|
|
" println!(\"Creating MyBox with some data!\");\n",
|
|
" MyBox(x)\n",
|
|
" }\n",
|
|
"}\n",
|
|
" \n",
|
|
"use std::ops::Deref;\n",
|
|
"\n",
|
|
"impl<T> Deref for MyBox<T> {\n",
|
|
" type Target = T;\n",
|
|
"\n",
|
|
" fn deref(&self) -> &Self::Target {\n",
|
|
" &self.0\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl<T> Drop for MyBox<T> {\n",
|
|
" fn drop(&mut self) {\n",
|
|
" println!(\"Dropping MyBox with some data!\");\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" let x = 5;\n",
|
|
" let y = MyBox::new(x);\n",
|
|
"\n",
|
|
" assert!(5 == x);\n",
|
|
" assert!(5 == *y);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Deref coercion\n",
|
|
"\n",
|
|
"* Deref coercion converts a reference to a type that implements the `Deref` trait into a reference to another type. For example, deref coercion can convert `&String` to `&str` because `String` implements the `Deref` trait such that it returns `&str`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Hello, John!\n",
|
|
"Hello, Jane!\n",
|
|
"Creating MyBox with some data!\n",
|
|
"Hello, Rust!\n",
|
|
"Dropping MyBox with some data!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"fn hello(name: &str) {\n",
|
|
" println!(\"Hello, {}!\", name);\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" hello(\"John\");\n",
|
|
" \n",
|
|
" let name = String::from(\"Jane\");\n",
|
|
" hello(&name);\n",
|
|
"\n",
|
|
" let m = MyBox::new(String::from(\"Rust\"));\n",
|
|
" hello(&m);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## `Rc` (Reference Counted Smart Pointer)\n",
|
|
"\n",
|
|
"* `Rc<T>` enables multiple ownership of the same data; `Rc<T>` keeps track of the number of references to a value which determines whether or not a value is still in use.\n",
|
|
"* `Rc<T>` is only for use in single-threaded scenarios. For multi-threaded code, `Arc<T>` should be used.\n",
|
|
"* `Rc<T>` points to immutable data. If you need mutability, put a `RefCell<T>` inside the `Rc<T>`."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Using Value(\"foo\"). RC: 3\n",
|
|
"Using Value(\"bar\"). RC: 2\n",
|
|
"v_inner - Value(\"bar\") - it was only one strong reference\n",
|
|
"Dropping Value(\"bar\")\n",
|
|
"Value(\"foo\")\n",
|
|
"---------------------------------\n",
|
|
"Dropping Value(\"foo\")\n",
|
|
"---------------------------------\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"use std::collections::HashMap;\n",
|
|
"use std::rc::Rc;\n",
|
|
"\n",
|
|
"#[derive(Debug)]\n",
|
|
"struct Value(String);\n",
|
|
"\n",
|
|
"impl Value {\n",
|
|
" fn value(&self) -> &String {\n",
|
|
" &self.0\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Drop for Value {\n",
|
|
" fn drop(&mut self) {\n",
|
|
" println!(\"Dropping {:?}\", self);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn use_value(data: Rc<Value>) {\n",
|
|
" println!(\"Using {:?}. RC: {}\", data, Rc::strong_count(&data));\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" let mut map = HashMap::<&str, Rc<Value>>::new();\n",
|
|
" {\n",
|
|
" let v_foo = Rc::new(Value(\"foo\".to_string()));\n",
|
|
" map.insert(\"foo\", v_foo.clone());\n",
|
|
" use_value(v_foo.clone());\n",
|
|
" {\n",
|
|
" let v_bar = Rc::new(Value(\"bar\".to_string()));\n",
|
|
" use_value(v_bar.clone());\n",
|
|
" \n",
|
|
" let v_inner = Rc::into_inner(v_bar);\n",
|
|
"\n",
|
|
" match v_inner {\n",
|
|
" Some(value) => {\n",
|
|
" println!(\"v_inner - {:?} - it was only one strong reference\", value);\n",
|
|
" }\n",
|
|
" None => {\n",
|
|
" println!(\"v_inner is None - more than one reference to it\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
" } \n",
|
|
" }\n",
|
|
"\n",
|
|
" println!(\"{:?}\", map.get(\"foo\").unwrap());\n",
|
|
"\n",
|
|
" println!(\"---------------------------------\");\n",
|
|
"\n",
|
|
" map.clear();\n",
|
|
"\n",
|
|
" println!(\"---------------------------------\");\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## `Weak` - Breaking Reference Cycles\n",
|
|
"\n",
|
|
"* `Weak<T>` is a version of `Rc<T>` that holds a non-owning reference. The object that `Weak<T>` points to is only valid if there is at least one `Rc<T>` pointing to it.\n",
|
|
"* `Weak<T>` is useful in breaking reference cycles. A reference cycle is a situation where two objects reference each other, preventing either from being cleaned up."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Memory Leak - Reference Cycles"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"John: John (30) is not married\n",
|
|
"Jane: Jane (25) is not married\n",
|
|
"Dropping Jane (25)\n",
|
|
"Dropping John (30)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"use std::cell::RefCell;\n",
|
|
"use std::fmt::{self, Display, Formatter, Debug};\n",
|
|
"\n",
|
|
"struct Human {\n",
|
|
" name: String,\n",
|
|
" age: u32,\n",
|
|
"\n",
|
|
" partner: RefCell<Option<Rc<Human>>>,\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Debug for Human {\n",
|
|
" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n",
|
|
" write!(f, \"{} ({})\", self.name, self.age)\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Display for Human {\n",
|
|
" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n",
|
|
" write!(f, \"{} ({})\", self.name, self.age);\n",
|
|
"\n",
|
|
" if let Some(partner) = &*self.partner.borrow() {\n",
|
|
" write!(f, \" is married to {}\", partner.name)\n",
|
|
" } else {\n",
|
|
" write!(f, \" is not married\")\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Drop for Human {\n",
|
|
" fn drop(&mut self) {\n",
|
|
" println!(\"Dropping {:?}\", self);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Human {\n",
|
|
" fn new(name: &str, age: u32) -> Rc<Human> {\n",
|
|
" Rc::new(Human {\n",
|
|
" name: name.to_string(),\n",
|
|
" age,\n",
|
|
" partner: RefCell::new(None),\n",
|
|
" })\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn marry(h1: Rc<Human>, h2: Rc<Human>) {\n",
|
|
" *h1.partner.borrow_mut() = Some(h2.clone());\n",
|
|
" *h2.partner.borrow_mut() = Some(h1.clone());\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" let mut john = Human::new(\"John\", 30);\n",
|
|
" let mut jane = Human::new(\"Jane\", 25);\n",
|
|
"\n",
|
|
" //marry(john.clone(), jane.clone()); // this creates a reference cycle -> memory leak\n",
|
|
"\n",
|
|
" println!(\"John: {}\", john);\n",
|
|
" println!(\"Jane: {}\", jane);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Breaking Reference Cycles with `Weak`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "rust"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"John: John (30) is married to Jane - Rc::strong_count(1)\n",
|
|
"Jane: Jane (25) is married to John - Rc::strong_count(1)\n",
|
|
"Dropping Jane (25)\n",
|
|
"John: John (30) is widow/widower\n",
|
|
"Dropping John (30)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"use std::rc::{Rc, Weak}\n",
|
|
"use std::cell::RefCell;\n",
|
|
"use std::fmt::{self, Display, Formatter};\n",
|
|
"\n",
|
|
"struct Human {\n",
|
|
" name: String,\n",
|
|
" age: u32,\n",
|
|
"\n",
|
|
" partner: RefCell<Option<Weak<Human>>>,\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Debug for Human {\n",
|
|
" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n",
|
|
" write!(f, \"{} ({})\", self.name, self.age)\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Display for Human {\n",
|
|
" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n",
|
|
" write!(f, \"{} ({})\", self.name, self.age);\n",
|
|
"\n",
|
|
" if let Some(partner) = &*self.partner.borrow() {\n",
|
|
" if let Some(partner) = partner.upgrade() {\n",
|
|
" write!(f, \" is married to {}\", partner.name)\n",
|
|
" } else {\n",
|
|
" write!(f, \" is widow/widower\")\n",
|
|
" }\n",
|
|
" } else {\n",
|
|
" write!(f, \" is not married\")\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Drop for Human {\n",
|
|
" fn drop(&mut self) {\n",
|
|
" println!(\"Dropping {:?}\", self);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"impl Human {\n",
|
|
" fn new(name: &str, age: u32) -> Rc<Human> {\n",
|
|
" Rc::new(Human {\n",
|
|
" name: name.to_string(),\n",
|
|
" age,\n",
|
|
" partner: RefCell::new(None),\n",
|
|
" })\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn marry(h1: Rc<Human>, h2: Rc<Human>) {\n",
|
|
" *h1.partner.borrow_mut() = Some(Rc::downgrade(&h2));\n",
|
|
" *h2.partner.borrow_mut() = Some(Rc::downgrade(&h1));\n",
|
|
"}\n",
|
|
"\n",
|
|
"fn main() {\n",
|
|
" let mut john: Rc<Human> = Human::new(\"John\", 30);\n",
|
|
" let mut jane: Rc<Human> = Human::new(\"Jane\", 25);\n",
|
|
"\n",
|
|
" marry(john.clone(), jane.clone());\n",
|
|
"\n",
|
|
" println!(\"John: {} - Rc::strong_count({})\", john, Rc::strong_count(&john));\n",
|
|
" println!(\"Jane: {} - Rc::strong_count({})\", jane, Rc::strong_count(&jane));\n",
|
|
"\n",
|
|
" std::mem::drop(jane);\n",
|
|
" println!(\"John: {}\", john);\n",
|
|
"}\n",
|
|
"\n",
|
|
"main();"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Rust",
|
|
"language": "rust",
|
|
"name": "rust"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": "rust",
|
|
"file_extension": ".rs",
|
|
"mimetype": "text/rust",
|
|
"name": "Rust",
|
|
"pygment_lexer": "rust",
|
|
"version": ""
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|