Rust Ownership and Borrowing

Ownership Rules and Borrowing Rules

Ownership Rules:

  1. Different Rust’s values have certainly a single owner.
  2. When an owner leaves a scope, the value becomes inaccessible.
  3. A value simply cannot possess more than one owner.

Example:

                    
fn main() {
    let s1 = String::from("hello"); // s1 owns the string
    let s2 = s1; // Ownership of the string is moved to s2
    // println!("{}", s1); // This line would cause an error because s1 no longer owns the string
    println!("{}", s2); // s2 owns the string
}
                    
                  

Borrowing Rules:

  1. You can take either immutable or mutable value, but you cannot take them at the same time.
  2. Each immutable incarnation can have several references, while only one mutable reference is allowed.
  3. References have to be valid.

Example:

                    
fn main() {
    let s1 = String::from("hello");

    // Immutable borrowing
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);

    // Mutable borrowing
    let mut s2 = String::from("hello");
    change(&mut s2);
    println!("s2 is now '{}'", s2);
}

fn calculate_length(s: &String) -> usize {
    s.len() // Immutable borrow
}

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

Lifetimes and References

Lifetimes:

Lifetimes are utilised to express the duration of time references should remain valid.

These prevent dangling references which occur when a reference is pointing to the data that doesn’t exists anymore.

Example:

                    
fn main() {
    let r;
    {
        let x = 5;
        r = &x; // Error: `x` does not live long enough
    }
    println!("r: {}", r);
}
                    
                  

The above code nowhere contains the life for x. This impair would be overcome by Rust forcing one to annotate lifetimes when necessary.

Lifetime Annotations:

Lifetime annotations tell Rust how the lifetimes of references relate to each other.

Example:

                    
fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

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

In this case, <a !>: it - a lifetime annotation. The lifetime of the returned reference will also have that same span of time, just like the x and y refernces.

Summary

  • Ownership: Manages memory by ensuring each value has one owner, which controls the value's lifecycle.
  • Borrowing: Allows references to a value without taking ownership, adhering to strict rules to ensure safety.
  • Lifetimes: Save the dead links in a database format to access upon requirement and avoid dangling references.

Rust's system of ownership, borrowing, and lifetime works in unison to offer memory safety settings without the need of a collector garbage collection which guarantees for efficient and safe memory management.