trainings/Rust_2026/rust-bs-2026-09-14/notebooks/10_smart_pointers.ipynb

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
}