liveBook · Manning

Borrow checker

As explained in Rust in Action: Systems programming concepts and techniques

The borrow checker is the Rust compiler component that verifies ownership, borrowing, and lifetime rules to prevent invalid memory access.

The borrow checker is the part of the Rust compiler that rejects illegal access to data. To understand its errors, track three things: who owns a value and cleans it up, how long accessing it remains valid, and whether another part of the program is borrowing it. A variable can still be in scope even after its value is no longer available. section 4.0 section 4.1.1

A status check that moves its satellite

Suppose check_status takes a CubeSat value. The first call with sat_a moves ownership into the function; the function drops that value when it returns. A second call using sat_a fails to compile, although the name still appears in main. The compiler reports E0382: use of moved value. Follow the value, not just the variable name. section 4.1.1

Listing 4.3 Checking the status of our integer-based CubeSats
 #[derive(Debug)]                     #1
 struct CubeSat {
   id: u64,
 }
 
 #[derive(Debug)]
 enum StatusMessage {
   Ok,
 }
 
 fn check_status(
   sat_id: CubeSat
 ) -> StatusMessage {                 #2
   StatusMessage::Ok
 }
 
 fn main() {
   let sat_a = CubeSat { id: 0 };     #3
   let sat_b = CubeSat { id: 1 };     #3
   let sat_c = CubeSat { id: 2 };     #3
 
   let a_status = check_status(sat_a);
   let b_status = check_status(sat_b);
   let c_status = check_status(sat_c);
   println!("a: {:?}, b: {:?}, c: {:?}", a_status, b_status, c_status);
 
   // "waiting" ...
   let a_status = check_status(sat_a);
   let b_status = check_status(sat_b);
   let c_status = check_status(sat_c);
   println!("a: {:?}, b: {:?}, c: {:?}", a_status, b_status, c_status);
 }

The repeated status checks show where the second attempt to pass each satellite occurs. section 4.1.1

error[E0382]: use of moved value: `sat_a`
  --> code/ch4-check-sats-2.rs:26:31
   |
 |   let a_status = check_status(sat_a);
   |                               ----- value moved here
...
 |   let a_status = check_status(sat_a);
   |                               ^^^^^ value used here after move
   |
   = note: move occurs because `sat_a` has type `CubeSat`,
   = which does not implement the `Copy` trait
 
...      #1
 
error: aborting due to 3 previous errors

Compare the compiler's “value moved here” and “value used here after move” locations. section 4.1.1

When the checker refuses a program, reconsider how long values must live and how access is shared:

  • Borrow a value owned elsewhere when another part of the program needs access. Borrows may be read-only or read-write; only one read-write borrow can exist at a time. section 4.0 section 4.6
  • Prefer shorter-lived values where possible. Duplicating a value with Clone or Copy can break an impasse; Rc<T> offers reference-counted sharing, and interior mutability permits changes through types that appear immutable. section 4.6

An unsafe block does not bypass the borrow checker. It places responsibility for certain memory-safety guarantees on the programmer, so it is not a shortcut for resolving an ownership error. section 5.1

How Rust in Action: Systems programming concepts and techniques covers it

How deep it goes
Works through a moved-value error and practical remedies, without detailing the checker's implementation.
Examples
Uses repeated CubeSat status checks and compiler error E0382 to contrast a variable's scope with its value's lifetime.
What it assumes
Assumes familiarity with basic functions and local variables while defining ownership, lifetime, and borrowing for newcomers.
What you can do after
Diagnose/debug with it: trace ownership moves and consider shorter-lived values, borrowing, duplication, or reference counting when access fails.
How it connects
Connects ownership and lifetimes to borrowing, Clone, Copy, reference counting, and interior mutability.

Also explained in

Compare how 4 books explain borrow checker →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust in Action: Systems programming concepts and techniques. The section links point to the parts of the book it draws on.