Rust Tutorial menu

Rust Ownership and Borrowing

Ownership is Rust's special way of managing memory safely without a garbage collector.

Why Ownership?

Programs need memory to store data. Some languages make you free memory yourself, which leads to bugs. Others use a garbage collector, which slows things down. Rust uses a third way: a set of ownership rules that the compiler checks.

The Three Rules

  1. Each value in Rust has one owner (a variable).
  2. There can be only one owner at a time.
  3. When the owner goes out of scope, the value is dropped (its memory is freed).

Example

rust
fn main() {
    {
        let city = String::from("Karachi");
        println!("{}", city);
    } // city goes out of scope here and its memory is freed
    // println!("{}", city); // error: city does not exist here
}
Output
Karachi

Moves

When you assign a String to another variable, the ownership moves. The first variable can no longer be used.

Example

rust
fn main() {
    let a = String::from("Workownix");
    let b = a;
    // println!("{}", a); // error: value borrowed here after move
    println!("{}", b);

    let c = b.clone();
    println!("{} and {}", b, c);
}
Output
Workownix
Workownix and Workownix

clone() makes a full, separate copy, so both variables are valid.

Simple types like integers, floats, bool and char are Copy types. They are copied, not moved, so let y = x; keeps x usable.

Ownership and Functions

Passing a String to a function also moves it:

Example

rust
fn show(text: String) {
    println!("{}", text);
}

fn main() {
    let name = String::from("Ayesha");
    show(name);
    // show(name); // error: name was moved into the first call

    let n = 5;
    let m = n;
    println!("{} {}", n, m);
}
Output
Ayesha
5 5

Borrowing with References

Moving every value into functions would be annoying. Instead, you can borrow a value with a reference, written &. The function can read it, but the owner keeps it.

Example

rust
fn count_letters(text: &String) -> usize {
    text.len()
}

fn main() {
    let course = String::from("Rust");
    let size = count_letters(&course);
    println!("{} has {} letters", course, size);
}
Output
Rust has 4 letters

Mutable References

To let a function change a borrowed value, use &mut. The variable must also be mut.

Example

rust
fn add_title(name: &mut String) {
    name.push_str(" Sahab");
}

fn main() {
    let mut teacher = String::from("Imran");
    add_title(&mut teacher);
    println!("{}", teacher);
}
Output
Imran Sahab

The Borrowing Rules

At any one time you can have either:

AllowedExample
Any number of shared references&x, &x, &x
Exactly one mutable reference&mut x

You cannot have both at the same time. This rule stops two parts of the program from changing the same data at once, a common cause of bugs.

Slices

A slice borrows part of a collection, without copying it:

Example

rust
fn main() {
    let text = String::from("Hello Karachi");
    let first = &text[0..5];
    let last = &text[6..];
    println!("{} / {}", first, last);

    let marks = [60, 70, 80, 90];
    let middle = &marks[1..3];
    println!("{:?}", middle);
}
Output
Hello / Karachi
[70, 80]

Exercise

Exercise

After let a = String::from("hi"); let b = a; which variable can you still use?

Learn Rust with a teacher

Join the Python Programming course at Workownix, online or in Orangi Town, Karachi, and get a certificate.

See the course Ask on WhatsApp