Rust, shared: the borrow checker

What is the borrow checker?

The Rust borrow checker is a compiler phase that applies cutting edge techniques to enforce rules about when a value is created or destroyed. By default, when a value comes into scope, it is 'owned' by the context that created it. This ownership can be shared or transferred. Sharing a value is called 'pass by reference'. Transferring ownership of a value is called a 'move'. These definitions also closely align with how a value will be allocated and treated in memory.

The rules that the borrow checker enforces are a combination of implied lifetimes that are inferred by the compiler, as well as explicit lifetimes that a user can define. A lifetime is defined as a range wherein a value is 'in' or 'out of' scope, and wherein a value can be used.

How to trick the borrow checker?

It is possible to trick the borrow checker. There are two general methods of doing so: the first is easily applicable, but requires use of unsafe operations that can come back to haunt you later. The second method requires some preparation, but is not significantly more difficult to use than the unsafe method, and has the benefit of safety guarantees and lack of possible undefined behavior.

How to trick the borrow checker with a pointer cast

This method is in general discouraged when compared to the safe method. However we will provide it here to explain the basic logical principle which is the same in both methods. What we will do to trick the borrow checker is to cast the value that we want to escape into a pointer, which will strip the value of its lifetime parameter, then cast it back to its original type. By doing this simple operation we will gain a new lifetime parameter that has a maximal scope and can be transferred out of normal possession rules around in the program.

unsafe { & *a.as_ptr() }
unsafe { &mut *a.as_ptr() }

How to trick the borrow checker with the standard library

RefCell, Ref, and RefMut are types in the standard library that safely implement the type of escaped values like those mentioned above in the unsafe example. There are several restrictions that remain, but the most pernicious of those is only that the type of the reference be changed to Ref or RefMut rather than just &T or &mut T. Usage is as follows:

let t:RefCell<T> = ...;
t.borrow()
let t:RefCell<T> = ...;
t.borrow_mut()

and that's all there is to it.