#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: usize,
}
impl Post {
// `mut` is a problem here!
fn view(&mut self) {
self.viewed_times += 1;
}
}
In Rust, values are immutable by default.
We can make them mutable with the mut
keyword.
What if we want just partial mutability? Can we do this?
Of course we can!
Our prime accomplices are Cell<T>
and RefCell<T>
.
We have some blog posts which have immutable content, and an incrementing view count.
Ideally, we would have a fn view(&self) → &'static str
to return the content, and increment the view count.
Cell
s#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: usize,
}
impl Post {
// `mut` is a problem here!
fn view(&mut self) {
self.viewed_times += 1;
}
}
Cell
sThis isn’t ideal! view
takes a &mut self
, meaning this won’t work:
fn main() {
let post = Post {
content: String::from("Blah"),
..Post::default()
};
(0..5).for_each(|_| post.view());
println!("{:?}", post);
}
// From before
#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: usize,
}
impl Post {
// `mut` is a problem here!
fn view(&mut self) {
self.viewed_times += 1;
}
}
Cell
sfn main() {
// We need to make the entire struct mutable!
let mut post = Post {
content: String::from("Blah"),
..Post::default()
};
(0..5).for_each(|_| post.view());
println!("{:?}", post);
}
// From before
#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: usize,
}
impl Post {
// `mut` is a problem here!
fn view(&mut self) {
self.viewed_times += 1;
}
}
Cell
Cell
lets us move and take values inside.
RefCell
works with references through 'dynamic borrowing'.
Let’s see our previous example with Cell
.
Cell
fn main() {
let post = Post {
content: String::from("Blah"),
..Post::default()
};
(0..5).for_each(|_| post.view());
println!("{:?}", post);
}
#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: Cell<usize>,
}
impl Post {
fn view(&self) {
// Note how we are making a copy, then replacing the original.
let current_views = self.viewed_times.get();
self.viewed_times.set(current_views + 1);
}
}
use std::cell::Cell;
RefCell
fn main() {
let post = Post {
content: String::from("Blah"),
..Post::default()
};
(0..5).for_each(|_| post.view());
println!("{:?}", post);
}
#[derive(Debug, Default)]
struct Post {
content: String,
viewed_times: RefCell<usize>,
}
impl Post {
fn view(&self) {
// Note how we're mutating a value.
*self.viewed_times.borrow_mut() += 1;
}
}
use std::cell::RefCell;
…interior mutability is something of a last resort.