Say we need a Cursor<T> type, which should contain a mutable reference to T. The type has a method .dup() that will create a new instance of Cursor<T> while sharing the reference between new and old instances. This is common in database driver designing, where users might expect to have multiple cursors alive simultaneously, owning references to the same connection object. A possible implementation might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Cursor<'a, T> {
obj: &'a mut T,
}
impl<'a, T> Cursor<'a, T> {
fn new(t: &'a mut T) -> Cursor<'a, T> {
Cursor { obj: t }
}
fn dup(&mut self) -> Cursor<T> {
Cursor { obj: self.obj }
}
}
fn main() {
let mut i = 1;
let mut cursor_a = Cursor::new(&mut i);
let _cursor_b = cursor_a.dup();
}

Perfect and neat, and luckily Rust compiler did not complain. As a Rust freshman, like me, it’s not easy to keep compiler silent all the time, especially when playing with references. But once I try to chain up the constructor and .dup(), the compiler screams:

1
2
3
4
5
fn main() {
let mut i = 1;
let a = Cursor::new(&mut i).dup();
println!("{:?}", a.obj);
}
1
2
3
4
5
6
7
8
9
|
21 | let a = Cursor::new(&mut i).dup();
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
22 | println!("{:?}", a.obj);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value

It’s confusing. In most languages such as Python, either splitting the calling or chaining them should have no difference, but Rust sometimes rejects the latter one.

To figure out the problem, let’s write the signature of .dup() in a more verbose way:

1
2
3
fn dup<'a>(&'a mut self) -> Cursor<'a, T> {
// ...
}

Rust compiler allows us to elide the lifetime specifier 'a. The signature implies that self should live at least as long as the returned object. But the chaining is in fact something like:

1
2
3
4
let a = {
let mut __a = Cursor::new(&mut i);
__a.dup()
};

The temporary variable __a lives only in the block, and is dropped at the end of it. But the result of __a.dup() is passed out of the block, and thus it has a longer lifetime than __a. This conflicts with the signature. To fix it, we might instead use a self-consuming version of .dup(), say .into_dup():

1
2
3
fn into_dup(self) -> Cursor<'a, T> {
Cursor { obj: self.obj }
}

.into_dup() moves all content in self into the new object, and makes no implication on the lifetime. Now we can write code like Cursor::new(&mut i).into_dup().

Rc

However, the solution above is not satisfactory enough. Apparently we don’t want to make a self-consuming version for each method, just in order to please the compiler. In fact, a smart pointer with reference counting may be more suitable for the job. So let’s rewrite it using std::rc::Rc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use std::rc::Rc;
struct Cursor<T> {
obj: Rc<T>,
}
impl<T> Cursor<T> {
fn new(t: &Rc<T>) -> Cursor<T> {
Cursor { obj: t.clone() }
}
fn dup(&self) -> Cursor<T> {
Cursor {
obj: self.obj.clone(),
}
}
}

An Rc instance could be regarded as a wrapper for a pointer (or reference), and cloning the instance implies duplicating the pointer. But from the perspective of compiler, there’s no borrowing involved, and no more need for explicit lifetime declarations in struct and impl. We can now use method chaining as expected:

1
2
3
4
5
6
7
8
fn main() {
let mut i = Rc::new(1);
let a = Cursor::new(&i).dup();
println!("i: {:?} a.obj: {:?}", a.obj, i);
}
// Output
// i: 1 a.obj: 1

Great! Looks like that Rc is an helper which relaxes the constraints of references, and allow us to do more flexible reference sharing.

Now let’s try something else — mutating the target through a reference:

1
2
3
4
5
6
fn main() {
let mut i = Rc::new(1);
let a = Cursor::new(&i).dup();
*Rc::get_mut(&mut i).unwrap() = 2;
println!("i: {:?} a.obj: {:?}", a.obj, i);
}

We can read the inner value by directly dereferencing an Rc<T> instance, as Rc implements the Deref trait. But when we want to mutably dereference it, we need to use Rc::get_mut. The function returns an Option<&mut T> instead of simply &mut T. This may be wierd at the first sight, since Rc objects own the target, and you can come up with no occasion that the reference might become invalid. Let’s just ignore it now and compile the code. The compiler complains nothing. But when we run the code, it panics:

1
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:347:21

The fact is that Rc::get_mut indeed returns a None! For some reason a mutable reference is not available at that time. Let’s turn to the docs for help:

(Rc::get_mut) Returns a mutable reference to the inner value, if there are no other Rc or Weak pointers to the same value.(Rc::get_mut) Returns None otherwise, because it is not safe to mutate a shared value.

Rc::get_mut will ensure there’s no other Rc or Weak instances referencing the target, and fails (returns None) otherwise. In the code above, we actually has two Rc instances, i and a.obj, referencing the inner value, so Rc::get_mut denies the borrow. Seems like it’s just another borrow checker! Except that Rc do the checking at runtime. We may make a quick comparison between Rcs and references:

Rc:

  • At any moment there may exists several Rc<T> instances referencing the same inner value. One can use * to directly deference one of them to immutably access the inner value.
  • An Rc<T> instance can be mutably dereferenced if and only if it is the only instance referencing the target.

Reference:

  • For a value v with type T, at any moment there may exists several immutable reference to v. Each can be derefenced and returns the pointed value.
  • An &mut T to v can exist, if and only if there is no other reference to v.

Yeah, Rust is safe… But is our design wrong? Not really. We may just misuse the underlying structure.

The problem is, the existence of an Rc<T> instance implies an “immutable reference” to the inner value, which blocks out mutable access to the target. However, this is not exactly what we want. Usually we hold a reference to an object because it may be (mutably or immutably) referenced in the future. The read/write actions may just occupy a very short period in the whole lifetime of program. It would be better if the checking is done at the time the inner value is going to be read.

RefCell and Interior Mutability Pattern

But we still need Rc, since it’s almost the only choice for reference sharing. The trick is that we should find some ways to mutate the inner value, while only given an immutable reference.

What makes Rust annoying is that, the concept mutability is “infectious”. If one want to modify a part of an object, he must have a mutable reference to the whole object. It’s inconvenient for objects which consist of several independent parts. Such design is not always bad, but it does not play well with the borrow checker. For more generic designs, sometimes mutability of an object should be hidden, so that we can mutate the object with only immutable reference given. This is what we called Interior Mutability Pattern in Rust.

RefCell is designed to compete the job. A RefCell instance has .borrow() and .borrow_mut() method for corresponding borrowing. Both the two methods have parameter &self, so there’s no requirement for the instance’s compile-time mutability. However, it will still panic if the borrowing break the rules, but for most of the time we can use it with no bothers.

Rc + RefCell

Let’s use Rc<RefCell<T>> to rewrite our code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::cell::RefCell;
use std::rc::Rc;
struct Cursor<T> {
obj: Rc<RefCell<T>>,
}
impl<T> Cursor<T> {
fn new(t: &Rc<RefCell<T>>) -> Cursor<T> {
Cursor { obj: t.clone() }
}
fn dup(&self) -> Cursor<T> {
Cursor {
obj: self.obj.clone(),
}
}
}
fn main() {
let i = Rc::new(RefCell::new(1));
let a = Cursor::new(&i).dup();
*i.borrow_mut() = 2;
println!("i: {:?} a.obj: {:?}", a.obj, i);
}

The declaration may look a bit verbose, but, it did work! We can now share the reference between objects, and as well mutate the inner value through a mutable borrowing. Note that because of the automatic dereference behavior in Rust, the Rc layer is in fact transparent and we can write code like i.borrow_mut(). This saves us from becoming overly verbose.

Now if we add an additional line:

1
2
3
4
5
6
7
fn main() {
let i = Rc::new(RefCell::new(1));
let a = Cursor::new(&i).dup();
let _ref = i.borrow(); // Another borrow here
*i.borrow_mut() = 2;
println!("i: {:?} a.obj: {:?}", a.obj, i);
}

The program still compiles, but will panic at runtime:

1
thread 'main' panicked at 'already borrowed: BorrowMutError', src/libcore/result.rs:999:5

Since at the moment of i.borrow_mut(), there already exists another immutable borrowing _ref, which break the rules. The example shows that RefCell still do the checking at runtime, so it’s safe enough to use.

References