Smart pointers

Contents
Box for size
Rc shared read
RefCell runtime checks
Rc with RefCell

Box for size

Box places one value on the heap and owns it outright. Recursive types need it because the compiler must know every size up front. A list node holds its tail inside a box, so each node measures the same few bytes.

enum List { Node(i32, Box<List>), End, } fn main() { let items = List::Node(1, Box::new(List::End)); println!("list built"); }

Without the box the enum would contain itself forever and sizing would never end. Dereferencing a box works like a plain reference. Reach for Box first whenever ownership stays single.

Rc shared read

Rc counts shared owners of one read only value. Cloning an Rc bumps the count instead of copying the data. The string below lives until both owners leave scope, then frees itself exactly once.

use std::rc::Rc; fn main() { let a = Rc::new(String::from("shared")); let b = Rc::clone(&a); println!("{} {}", a, b); }

Rc never allows mutation, which is what makes sharing safe. Cloning is cheap no matter how large the data grows. Threads need the atomic sibling Arc, covered in a later lesson.

RefCell runtime checks

RefCell moves borrow checking from compile time to run time. Code borrows mutably through a shared reference, and bad timing panics instead of failing to compile. The counter below starts at five and climbs by one through a mutable borrow.

use std::cell::RefCell; fn main() { let cell = RefCell::new(5); *cell.borrow_mut() += 1; println!("{}", *cell.borrow()); }

Borrow and borrow mut hand out guards that release on drop. Holding two mutable guards at once panics, so keep each borrow short. Interior mutability unlocks patterns that static borrows reject.

Rc with RefCell

Pairing Rc with RefCell gives shared ownership plus mutation. Many owners point at one cell, and any of them may update through it. The counter below climbs through the shared handle with no owner left out.

use std::cell::RefCell; use std::rc::Rc; fn main() { let shared = Rc::new(RefCell::new(0)); *shared.borrow_mut() += 1; println!("{}", *shared.borrow()); }

The outer Rc shares while the inner cell mutates, and each layer does one job well. Single threaded code stops here, since Rc never crosses threads. The Arc plus Mutex pair brings this pattern to threaded programs.

Next: Threads

Article author: Arthur Isaev

Related articles
Rust
Rust course: zero to hero in 50 lessons
What is Rust and where it runs
Install Rust
cargo new
Your first program
Variables
Numbers
Strings
Booleans
Tuples
Arrays
Vectors
Slices
if and else
match
Loops
Ownership
Borrowing
Lifetimes
Functions
Structs
Enums in Rust
Advanced match in Rust
Option combinators in Rust
Result and the question mark in Rust
Advanced strings in Rust
Vec methods in Rust
HashMaps in Rust
Modules in Rust
Crates and workspaces in Rust
Testing in Rust
Error handling
Traits
Generics
Trait bounds
Closures
Iterators
Smart pointers
Threads
Mutex and Arc
Files
CLI arguments
Environment variables
Diagnostics
Time
Parsing strings
TCP networking
Capstone: wordfreq CLI
Capstone: tiny HTTP server
Modern Rust tour
Hero roadmap: the whole course on one page
Install Rust on Windows 11
Install Rust on Ubuntu
Install Rust on Rocky Linux
Install Rust on macOS
Install Rust on FreeBSD
Rust in Docker

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: