Say we need a type Cursor<T> , which contains a mutable reference to T . The type has a method, say .dup() , which returns a new instance of Cursor<T> , while sharing the interior reference across instances. Such design is a common pattern in database driver library. Users might expect to hold multiple cursors simultaneously, each of which owns a reference 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 freshman in Rust, 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 wierd. Chaining callings or not should make no difference, at least in most languages — but not in Rust.

To figure out the problem, let’s expand the signature of .dup() into 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. However, by chaining the callings, the code is in fact equivalent to:

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

The temporary variable __a lives only within the block, and will be dropped at the end of it. But the return value of __a.dup() is passed out of the block, and thus it has a longer lifetime than __a , which violates the constraints. 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

The solution above is not satisfactory enough. Apparently we won’t make a self-consuming version for each method, just to please the compiler. In fact, a smart pointer with reference counting may be more suitable for the job. Let’s try rewriting the code with 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 wraps a pointer (or reference) internally, 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 chain the callings 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 a 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 via 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);
}

The 4-th line is more verbose than expected. Rc implements the Deref trait only, but not DerefMut . Instead, it provides Rc::get_mut for mutable dereferencing, which returns an Optional<&mut T> . The Optional wrapper implies a possible failure during dereferencing. This may be wierd at the first sight. An Rc<T> object owns a T object, so the reference should never be invalid.

Let’s ignore the question for a second and continue. The compiling passes smoothly, but we get a panic at runtime:

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

Oops… Rc::get_mut really returns a None ! To explain this, we might go back 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.

The fact is that, Rc::get_mut will perform a run-time check to ensure there’s no other Rc or Weak instances referencing the target, and fails (returns None ) if so. In the code above, there are actually two Rc instances, i and a.obj , sharing a same value internally. Consequently, Rc::get_mut denies the dereferencing. From this perspective, Rc resembles another borrow checker at runtime.

We might do a quick comparison between Rc s and primitive references:Rc :

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

Reference:

  • For a value v with type T , at any moment there may exist 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, existence of an Rc<T> instance implies an “immutable reference” to the internal value, and thus disallows mutable access to it in the future. The implication drives out data races, but is too aggressive for our scenario. In some occasions, accessing / writing an object will never collide. A better design is deferring the checking to the time interval value being accessed.

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 internal value with only an immutable reference provided.

In Rust, the concept mutability is “infectious”. Even if we just attempt to partially mutate an object, a mutable reference to the complete object is still needed. It’s inconvenient for objects consisting of several independent parts. Such design is not always bad, but it does not play well with the borrow checker. For more generic scenarios, sometimes mutability of an object should be hidden, so that it can be mutated with only immutable reference provided. This is what we called Interior Mutability Pattern in Rust.

The corresponding underlying structure is RefCell . A RefCell instance has .borrow() and .borrow_mut() method for immutable or mutable borrowing. Both of the two methods have argument &self , and thus no requirement for compile-time mutability. They will still panic if the borrowing violate the rules, but for most of the time, they can be used without any bother.

Rc + RefCell

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

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 get a little more verbose, but, it does work! We can now share the reference between objects, and mutate the interval value via an immutable borrowing. Thanks to the automatic dereference in Rust, the Rc layer is in fact transparent and we can write statement like i.borrow_mut() , which reduces verbosity to some extend.

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

At the moment of i.borrow_mut() , there already exists another immutable borrowing _ref , which consequently violates the rules. We can see that RefCell relaxes compile-time constraints, but is still safe enough at runtime.

References