Rust Language

Rust Ownership and Borrowing: A Complete Guide for Beginners to Intermediate Users

Rust is a language that allows programs to run safely and efficiently without requiring manual memory management. At the core of Rust's memory safety model are the concepts of Ownership and Borrowing. This article provides a detailed explanation of ownership and borrowing, including practical code examples, to help beginners and intermediate users understand these fundamental concepts.

1. What is Ownership?

Rust's ownership system is based on three fundamental rules:

  1. Each value in Rust has a single owner.
  2. When the owner goes out of scope, the value is automatically dropped.
  3. Ownership can be transferred (moved), but a value can only have one owner at a time.

These rules ensure memory safety without requiring a garbage collector.

1.1 Moving Ownership

Ownership is transferred when a variable is assigned to another variable. In the following example, the ownership of a String moves from s1 to s2:

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{}", s1); // Compilation error
}

Since s1's ownership has moved to s2, s1 is no longer valid. This restriction prevents double-free errors.

1.2 Cloning to Copy Data

To copy a value instead of transferring ownership, we use the clone method:

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("{}", s1); // No issue
}

Using clone(), a new memory allocation is made, allowing both variables to remain valid.

1.3 Copying Stack-Based Types

Primitive types, such as i32, are stored on the stack and implement the Copy trait. This means they are copied rather than moved when assigned:

fn main() {
    let x = 5;
    let y = x; // `x` remains valid
    println!("{}", x);
}

1.4 Ownership and Functions

Ownership also transfers when passing values to functions:

fn takes_ownership(s: String) {
    println!("{}", s);
} // `s` is dropped here

fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    println!("{}", s); // Error: ownership moved
}

When s is passed to takes_ownership, its ownership moves into the function, making it invalid in main.

2. What is Borrowing?

Borrowing allows references to a value without transferring ownership. Rust has two types of borrowing:

  1. Immutable Borrowing
  2. Mutable Borrowing

2.1 Immutable Borrowing

An immutable borrow allows reading a value without modifying it.

fn print_length(s: &String) {
    println!("Length: {}", s.len());
}

fn main() {
    let s = String::from("hello");
    print_length(&s);
    println!("{}", s); // No issue
}

Multiple immutable borrows are allowed at the same time.

2.2 Mutable Borrowing

A mutable borrow allows modifying a value, but only one mutable reference is permitted at a time:

fn change(s: &mut String) {
    s.push_str(" world");
}

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s); // hello world
}

This rule prevents data races and ensures safe concurrent access.

3. Lifetimes and Borrowing

Lifetimes define how long a reference remains valid. By specifying lifetimes, the Rust compiler ensures that references remain valid throughout their usage.

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

fn main() {
    let s1 = "long string";
    let s2 = "short";
    let result = longest(s1, s2);
    println!("{}", result);
}

By specifying 'a as the lifetime parameter, Rust ensures that both s1 and s2 outlive the returned reference.

4. Summary of Ownership and Borrowing

  • Each value has only one owner, and ownership transfers on assignment or passing to a function.
  • Moving ownership invalidates the original variable.
  • Cloning creates a new copy, allowing both values to remain valid.
  • Immutable borrowing allows multiple simultaneous references.
  • Mutable borrowing allows modification but only one reference at a time.
  • Lifetimes ensure that references remain valid for a defined scope.