To illustrate the necessity of Rc<T>
, the Book presents the following snippet (spoiler: it won't compile) to show that we cannot enable multiple ownership without Rc<T>
.
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a));
}
It then claims (emphasis mine)
We could change the definition of
Cons
to hold references instead, but then we would have to specify lifetime parameters. By specifying lifetime parameters, we would be specifying that every element in the list will live at least as long as the entire list. The borrow checker wouldn’t let us compilelet a = Cons(10, &Nil);
for example, because the temporaryNil
value would be dropped before a could take a reference to it.
Well, not quite. The following snippet compiles under rustc 1.52.1
enum List<'a> {
Cons(i32, &'a List<'a>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let a = Cons(5, &Cons(10, &Nil));
let b = Cons(3, &a);
let c = Cons(4, &a);
}
Note that by taking a reference, we no longer need a Box<T>
indirection to hold the nested List
. Furthermore, I can point both b
and c
to a
, which gives a
multiple conceptual owners (which are actually borrowers).
Question: why do we need Rc<T>
when immutable references can do the job?
List
only borrows its values you're going to have trouble returning a populatedList
from a function for example. – ManlyCons(i32, &'a Box<List<'a>>),
... – Retiarius