Learning Rust's Ownership and Borrowing: Learn from the Book "Rust in Action"

Learning Rust's Ownership and Borrowing: Learn from the Book "Rust in Action"

Apr 17, 2023·

4 min read

In Rust, every variable has ownership, which defines when the variable can be used and modified. When ownership of a variable is transferred, its lifetime is also transferred. This allows Rust to check ownership and lifetimes of code at compile-time to prevent unexpected errors at runtime. Recently, I read a book called "Rust in Action," which had a chapter on ownership that gave me a deeper understanding of this topic.

Using references when ownership is not necessary

Using borrowing can avoid unnecessary ownership transfers, while also improving code readability and safety. For example, if a variable only needs to be read in a function, it can be passed as a parameter to the function without transferring ownership to the function. This can prevent the risk of forgetting to release memory after the function call ends. In addition, borrowing can allow multiple variables to share the same memory, reducing memory usage.

Use fewer long-lived values

In Rust, when a variable has ownership, it is responsible for releasing resources. Therefore, when a variable no longer needs ownership, returning ownership to release resources can effectively avoid wasting resources. In fact, even when ownership of a variable is not needed, the variable can still be used. This is useful for situations where resource ownership needs to be passed.

Let's consider an example of a classroom and students. In this example, the classroom is long-lived data, while the students are short-lived data. When a student needs to leave the classroom, there is no need to destroy the classroom, only to return ownership of the student.

struct Student {
    name: String,

impl Student {
    fn new(name: String) -> Student {
        Student { name }

    fn leave(&self) {
        println!("{} has left the classroom", self.name);

struct Classroom {
    students: Vec<Student>,

impl Classroom {
    fn new() -> Classroom {
        Classroom { students: vec![] }

    fn add_student(&mut self, student: Student) {

    fn remove_student(&mut self, student: &Student) {
        self.students.retain(|s| s.name != student.name);

fn main() {
    let mut classroom = Classroom::new();

    let alice = Student::new("Alice".into());
    let bob = Student::new("Bob".into());


    let alice = Student::new("Alice".into());

Wrap data within specialty types:

We can also use Rc<RefCell<T>> to share a struct and modify its value when needed.

In the example, Thermometer is a struct with a temperature value.

  1. We create a variable thermometer of type Rc<RefCell<Thermometer>>.

  2. We use the borrow_mut() method to modify the temperature value. Note that we use borrow_mut() instead of borrow() because we need to make changes to the temperature value.

  3. In this example, we modify the temperature value to 25.0.

  4. We use the clone() method to clone thermometer, so we can access the temperature value without owning thermometer.

  5. We use the borrow() method to obtain an immutable reference to thermometer, and call the get_temperature() method to obtain the temperature value. Note that we use borrow() instead of borrow_mut() because we do not need to modify the temperature value.


use std::cell::RefCell; use std::rc::Rc;

#[derive(Debug)] struct Thermometer { temperature: f64, }

impl Thermometer { fn new(temperature: f64) -> Self { Thermometer { temperature } }

fn get_temperature(&self) -> f64 { self.temperature }

fn set_temperature(&mut self, temperature: f64) { self.temperature = temperature; } }

fn main() { // 1 let thermometer = Rc::new(RefCell::new(Thermometer::new(20.0))); // 4 // clone Rc for future use let thermometer_clone = Rc::clone(&thermometer);

// modify temperature through Rc> { // 2 let mut borrowed_thermometer = thermometer.borrow_mut(); // 3 borrowed_thermometer.set_temperature(25.0); }

// 4 // access temperature through Rc> let temperature = thermometer_clone.borrow().get_temperature();

//The current temperature is 25. println!("The current temperature is {}.", temperature); }


Ownership is an important feature of the Rust language that allows Rust to ensure memory safety and avoid common concurrency issues at compile time. In Rust, variables must be passed or moved for ownership, rather than simply copied or referenced. Through the concept of the ownership model, Rust provides a powerful way for programmers to manage resources in their programs. If you want to dive deeper into Rust's ownership features, it is recommended to read the book Rust in Action. This book not only provides a detailed introduction to the concept of ownership but also offers rich examples and code samples to help readers better understand how ownership is used in Rust.