Skip to content

Commit 0ea7898

Browse files
committedMar 8, 2025
pointers
1 parent cef70f0 commit 0ea7898

File tree

5 files changed

+425
-0
lines changed

5 files changed

+425
-0
lines changed
 

‎README.md

+12
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ cargo build
192192
- [ ] [`Fn`, `FnMut`, `FnOnce`](./src/bin/fn_traits.rs)
193193
- [ ] [Closure as input and output](./src/bin/closure_out.rs)
194194

195+
### Smart pointers
196+
197+
- [x] [`Box`](./src/bin/box.rs)
198+
- recursive data structure
199+
- unbox (get value inside box)
200+
- `Box<dyn Error>`
201+
- [x] [`Rc`](./src/bin/rc.rs)
202+
- [x] [`RefCell`](./src/bin/ref_cell.rs)
203+
- interior mutability
204+
- [x] [Strong and weak references](./src/bin/weak.rs)
205+
- `Weak`
206+
195207
### Concurrency
196208

197209
- [ ] [thread](./src/bin/thread.rs)

‎src/bin/box.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#![allow(unused)]
2+
3+
// Smart pointer
4+
// Pointer with metadata and additional capabilities
5+
6+
// Box
7+
// - Allows data to be stored on the heap
8+
// - Useful for data where the size is not known at compile time
9+
// - Trait objects
10+
// - Recursive data structure
11+
12+
// Box<dyn Error> = trait object that implements the Error trait
13+
fn f() -> Result<(), Box<dyn std::error::Error>> {
14+
Ok(())
15+
}
16+
17+
// Recursive data structures
18+
#[derive(Debug)]
19+
enum List {
20+
Cons(i32, Box<List>),
21+
Nil,
22+
}
23+
24+
#[derive(Debug)]
25+
struct Tree {
26+
val: i32,
27+
left: Option<Box<Tree>>,
28+
right: Option<Box<Tree>>,
29+
}
30+
31+
use crate::List::{Cons, Nil};
32+
33+
fn main() {
34+
// Box - allocate data to heap
35+
let b: Box<i32> = Box::new(0);
36+
// Dereference to get the inner value
37+
let v = *b;
38+
println!("box: {}", v);
39+
40+
// List
41+
// 3 -> 2 -> 1 -> Nil
42+
let mut list = Cons(3, Box::new(Cons(2, Box::new(Cons(1, Box::new(Nil))))));
43+
// Example - print all values in list
44+
while let Cons(i, tail) = list {
45+
print!("{} -> ", i);
46+
// Dereference a box
47+
list = *tail;
48+
}
49+
println!("Nil");
50+
51+
// Tree
52+
let tree = Tree {
53+
val: 1,
54+
left: Some(Box::new(Tree {
55+
val: 2,
56+
left: None,
57+
right: Some(Box::new(Tree {
58+
val: 4,
59+
left: None,
60+
right: None,
61+
})),
62+
})),
63+
right: Some(Box::new(Tree {
64+
val: 3,
65+
left: None,
66+
right: None,
67+
})),
68+
};
69+
// No need to dereference Box<Tree>
70+
// Rust automatically dereferences struct fields
71+
println!(
72+
"tree.left.right.val: {:?}",
73+
tree.left.unwrap().right.unwrap().val
74+
);
75+
}

‎src/bin/rc.rs

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![allow(unused)]
2+
3+
use std::rc::Rc;
4+
5+
// Rc - reference counting
6+
// - Use to share ownership for read only purpose
7+
// - Keeps track of the number of references to the value wrapped in Rc
8+
// - Reference count increases by 1 when Rc is cloned,
9+
// decreases by 1 when cloned Rc is dropped
10+
// - Cloning a Rc never performs a deep copy
11+
// - Single threaded use
12+
13+
// Box takes ownership, cannot share ownership of List
14+
// Lifetime is tricky to manage
15+
/*
16+
#[derive(Debug)]
17+
enum List<'a> {
18+
Cons(i32, Box<&'a List<'a>>),
19+
Nil,
20+
}
21+
*/
22+
23+
// Use Rc to share ownership of List
24+
#[derive(Debug)]
25+
enum List {
26+
Cons(i32, Rc<List>),
27+
Nil,
28+
}
29+
30+
use crate::List::{Cons, Nil};
31+
32+
fn main() {
33+
/*
34+
// Cannot create lists that fork at b
35+
// Cannot share ownership of b with c and d
36+
// a = 1 <- nil
37+
// b = 2 <- a
38+
// c = 3 <- b
39+
// d = 4 <- b
40+
let nil = Nil;
41+
let a = Cons(1, Box::new(&nil));
42+
let b = Cons(2, Box::new(&a));
43+
let c = Cons(3, Box::new(&b));
44+
let d = Cons(4, Box::new(&b));
45+
*/
46+
47+
// 2 -> 1 -> Nil
48+
let a = Rc::new(Cons(2, Rc::new(Cons(1, Rc::new(Nil)))));
49+
println!("a: {}", Rc::strong_count(&a));
50+
51+
// 3 -> a
52+
let b = Cons(3, Rc::clone(&a));
53+
// strong_count increases by 1
54+
println!("b: {}", Rc::strong_count(&a));
55+
56+
{
57+
// 4 -> a
58+
let c = Cons(4, Rc::clone(&a));
59+
// strong_count increases by 1
60+
println!("c: {}", Rc::strong_count(&a));
61+
}
62+
63+
// strong_count decreases by 1
64+
println!("drop c: {}", Rc::strong_count(&a));
65+
66+
// Example - print all values in List
67+
let mut curr = Rc::clone(&a);
68+
while let Cons(v, ref tail) = *curr {
69+
print!("{v} -> ");
70+
// tail = &Rc<List>
71+
if let Nil = **tail {
72+
break;
73+
}
74+
curr = Rc::clone(tail);
75+
}
76+
println!("Nil");
77+
}

‎src/bin/ref_cell.rs

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#![allow(unused)]
2+
3+
use std::cell::{RefCell, RefMut};
4+
use std::rc::Rc;
5+
6+
// Interior mutability
7+
// - Allows data mutatation even when there are immutable references to that data
8+
// - RefCell
9+
// - Interior mutability
10+
// - Run time error when borrowing rules are broken
11+
// - Single threaded use
12+
// - RefCell is used in combination with Rc to create a mutable data with shared ownership
13+
14+
#[derive(Debug)]
15+
enum List {
16+
Cons(Rc<RefCell<i32>>, Rc<List>),
17+
Nil,
18+
}
19+
20+
#[derive(Debug)]
21+
struct Node {
22+
val: u32,
23+
neighbors: RefCell<Vec<Rc<Node>>>,
24+
}
25+
26+
use crate::List::{Cons, Nil};
27+
28+
fn main() {
29+
// This will not compile
30+
// Cannot borrow mut on immutable value
31+
// let s = String::from("rust");
32+
// let s1 = &mut s;
33+
// s1 += "🦀";
34+
35+
// RefCell
36+
let s = String::from("rust");
37+
let r: RefCell<String> = RefCell::new(s);
38+
{
39+
let mut r1 = r.borrow_mut();
40+
*r1 += "🦀";
41+
// r is borrowed until r1 is dropped
42+
println!("{:#?}", r);
43+
44+
// Run time error - already borrowed
45+
// let mut r2 = r.borrow_mut();
46+
47+
// r1 is dropped
48+
}
49+
50+
// s is owned by r
51+
println!("{:#?}", r);
52+
53+
// Example - List
54+
// 1 -> Nil
55+
let mut a = Rc::new(Cons(Rc::new(RefCell::new(1)), Rc::new(Nil)));
56+
// 2 -> a
57+
let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
58+
// 3 -> a
59+
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
60+
61+
// Example - traverse to update last element
62+
let mut curr = Rc::clone(&a);
63+
while let Cons(v, tail) = &*curr {
64+
print!("{} -> ", *(v.borrow()));
65+
if let Nil = **tail {
66+
let mut x = v.borrow_mut();
67+
*x += 100;
68+
break;
69+
}
70+
curr = Rc::clone(tail);
71+
}
72+
println!("Nil");
73+
74+
println!("a: {a:?}");
75+
println!("b: {b:?}");
76+
println!("c: {c:?}");
77+
78+
// Example - update last element without traversing
79+
let v = Rc::new(RefCell::new(1));
80+
// 1 -> Nil
81+
let a = Rc::new(Cons(Rc::clone(&v), Rc::new(Nil)));
82+
// 2 -> a
83+
let b = Cons(Rc::new(RefCell::new(2)), Rc::clone(&a));
84+
// 3 -> a
85+
let c = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
86+
{
87+
// Need to create new scope to drop x after update
88+
let mut x: std::cell::RefMut<'_, i32> = (*v).borrow_mut();
89+
*x += 100;
90+
}
91+
// Same as
92+
// *(v.borrow_mut()) += 100;
93+
94+
println!("a: {a:?}");
95+
println!("b: {b:?}");
96+
println!("c: {c:?}");
97+
98+
// Example - Node
99+
/*
100+
#[derive(Debug)
101+
struct Node<'a> {
102+
pub val: u32,
103+
pub neighbors: Vec<&'a Node<'a>>,
104+
}
105+
106+
let mut node0 = Node {
107+
val: 0,
108+
neighbors: vec![],
109+
};
110+
let mut node1 = Node {
111+
val: 1,
112+
neighbors: vec![],
113+
};
114+
// Does not compile
115+
// node 0 -> node 1 (immutable borrow of node 1)
116+
node0.neighbors.push(&node1);
117+
// node 1 -> node 0 (mutable borrow of node 1)
118+
node1.neighbors.push(&node0);
119+
*/
120+
121+
let node0 = Rc::new(Node {
122+
val: 0,
123+
neighbors: RefCell::new(vec![]),
124+
});
125+
let node1 = Rc::new(Node {
126+
val: 1,
127+
neighbors: RefCell::new(vec![]),
128+
});
129+
130+
{
131+
// node 0 -> node 1
132+
let mut r0: RefMut<'_, Vec<Rc<Node>>> = node0.neighbors.borrow_mut();
133+
r0.push(Rc::clone(&node1));
134+
135+
// node 1 -> node 0
136+
let mut r1: RefMut<'_, Vec<Rc<Node>>> = node1.neighbors.borrow_mut();
137+
r1.push(Rc::clone(&node0));
138+
}
139+
140+
// Infinite loop - panics
141+
println!("{:?}", node0);
142+
}

‎src/bin/weak.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#![allow(unused)]
2+
3+
use std::cell::{RefCell, RefMut};
4+
use std::rc::{Rc, Weak};
5+
6+
// Strong reference shares ownership
7+
// Weak reference breaks reference cycles and avoid memory leaks
8+
9+
#[derive(Debug)]
10+
11+
struct Node {
12+
val: u32,
13+
neighbors: RefCell<Vec<Weak<Node>>>,
14+
}
15+
16+
fn main() {
17+
// Strong reference
18+
// Rc::clone -> strong_count increases
19+
// r0 is only dropped when strong_count is 0
20+
let x = "hello".to_string();
21+
22+
let r0: Rc<String> = Rc::new(x);
23+
println!("r0 - strong count: {}", Rc::strong_count(&r0));
24+
25+
let r1 = Rc::clone(&r0);
26+
println!("r1 - strong count: {}", Rc::strong_count(&r0));
27+
28+
// Weak refenence
29+
// Rc::downgrade -> weak_count increases
30+
// Rc::upgrade -> returns Option<Rc<T>>`
31+
let w1: Weak<String> = Rc::downgrade(&r0);
32+
println!("w1 - strong count: {}", Rc::strong_count(&r0));
33+
println!("w1 - weak count: {}", Rc::weak_count(&r0));
34+
35+
let w2: Weak<String> = Rc::downgrade(&r0);
36+
println!("w2 - strong count: {}", Rc::strong_count(&r0));
37+
println!("w2 - weak count: {}", Rc::weak_count(&r0));
38+
39+
// Upgrade weak to strong reference
40+
let u0 = w1.upgrade();
41+
println!("u0 - upgrade w1: {:?}", u0);
42+
println!("u0 - strong count: {}", Rc::strong_count(&r0));
43+
println!("u0 - weak count: {}", Rc::weak_count(&r0));
44+
45+
// Drop some strong references
46+
println!("drop u0 and r1");
47+
std::mem::drop(u0);
48+
std::mem::drop(r1);
49+
50+
let u1 = w1.upgrade();
51+
println!("u1 - upgrade w1: {:?}", u1);
52+
println!("u1 - strong count: {}", Rc::strong_count(&r0));
53+
println!("u1 - weak count: {}", Rc::weak_count(&r0));
54+
55+
// Drop all strong references
56+
println!("drop u1 and r0");
57+
std::mem::drop(u1);
58+
std::mem::drop(r0);
59+
60+
let u2 = w1.upgrade();
61+
println!("u2 - upgrade w1: {:?}", u2);
62+
63+
// Example - Node
64+
let node0 = Rc::new(Node {
65+
val: 0,
66+
neighbors: RefCell::new(vec![]),
67+
});
68+
let node1 = Rc::new(Node {
69+
val: 1,
70+
neighbors: RefCell::new(vec![]),
71+
});
72+
73+
// Create a cycle
74+
// node 0 -> node 1
75+
// node 1 -> node 0
76+
{
77+
// Rc::clone increments strong_count by 1.
78+
// Rc<T> cannot be dropped unless strong_count is 0.
79+
// Since node 0 and node 1 references each other, their strong_count can never reach 0.
80+
// Hence neither node 0 nor node 1 can ever be dropped.
81+
82+
// Weak<T> does not increment strong_count.
83+
// Hence both node 0 and 1 can be dropped.
84+
85+
// node 0 -> node 1
86+
let mut r0: RefMut<'_, Vec<Weak<Node>>> = node0.neighbors.borrow_mut();
87+
r0.push(Rc::downgrade(&node1));
88+
89+
// node 1 -> node 0
90+
let mut r1: RefMut<'_, Vec<Weak<Node>>> = node1.neighbors.borrow_mut();
91+
r1.push(Rc::downgrade(&node0));
92+
}
93+
94+
// No infinite loop
95+
println!("{:#?}", node0);
96+
97+
// Print node1 - prints Some(Node)
98+
println!(
99+
"{:#?}",
100+
node0
101+
.neighbors
102+
.borrow()
103+
.get(0)
104+
.map(|weak_ref| weak_ref.upgrade())
105+
);
106+
107+
// Drop node1
108+
std::mem::drop(node1);
109+
110+
// Print node1 - prints None
111+
println!(
112+
"{:#?}",
113+
node0
114+
.neighbors
115+
.borrow()
116+
.get(0)
117+
.map(|weak_ref| weak_ref.upgrade())
118+
);
119+
}

0 commit comments

Comments
 (0)
Please sign in to comment.