# Ownership

At one point in time, only one variable may own a piece of data. If a given variable holds data on a stack, the data is just copied:

let a = 4;
let b = a;
// there will be two "4" on the stack

In case of heap data, only the pointer is copied:

let a = String::from("abc");
let b = a;

// a is no longer a valid variable! b is the owner now!

Unique to Rust, the a variable becomes invalid when the code above gets executed. If we try to access it, it will result in a panic. Thanks to it, during runtime Rust will not try to drop (free) this memory space twice, which would be wrong. It is called a move. a gets moved to b.

Passing a value to a function also causes a move!

fn main() {
  let s = String::from("abc");
  some_func(s);
  // s is no longer valid. The ownership was moved!
}

fn some_func(data: String) {
  // At the end, `data` goes out of scope and `drop` is called
}

A function may also move ownership by returning a value. Then, the calling function's scope owns the variable.

# References

Sometimes we want to pass a value to a function, but also to continue to use it in the calling function. We could make the called function return the passed-in value, but that would be a weird workaround, especially if that function is supposed to return some other data as well. References come to the rescue.

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

  // pass as a reference
  let len = calculate_length(&s1);

  println!("The length of '{}' is {}.", s1, len);
  // we can still use `s1`
}

fn calculate_length(s: &String) -> usize {
  s.len()
  // s has no ownership. When it goes out of scope, nothing happens
}

Creating a reference is called borrowing. References are immutable by default. We can change that:

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

  change(&mut s);
}

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

Both the s variable and the some_string reference need to use the mut keyword to allow mutations of the value.

WARNING

At any given time, you can have either:

  • one mutable reference
  • any number of immutable references.

This will fail:

let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s; // Wrong!
println!("{}, {}", r1, r2);

Also, if there is an immutable reference, another one that is mutable cannot be created:

let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{}, {}, and {}", r1, r2, r3);

That's because if one function would get an immutable reference, it will expect that the value should not change suddenly.

TIP

A reference’s scope starts from where it is introduced and continues through the last time that reference is used. This is OK:

let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s; // It's OK!
println!("{}", r2);

r1 gets created, but it is never used afterwards. r2 may be created.

With Rust, it's impossible to have dangling pointers. Rust will complain of such issues at compile time.

# Dereferencing

let x = 5;
let y = &y;

assert_eq!(5, x);
assert_eq!(5, y); // WRONG! i32 and &i32 are different types

Sometimes we might need to obtain an actual value that the reference is pointing to. We can do that we the dereferencing operator:

let x = 5;
let y = &x;

assert_eq!(5, *y); // OK

Smart Pointers

Smart pointers might be dereferenced as well.

# Slices

Slice is a part of some collection. It is basically a pointer to some index of that collection and a count of elements starting from that index.

let a = [1, 2, 3, 4, 5];

let slice = &a[1..3];

# String Slice

Sometimes we need to "extract" a part of a string. Instead of creating a totally new string, disconnected from the original one, we can use a String Slice.

let s = String::from("abcd");

let slice = &s[1..3];

slice contains the pointer to the first character of the slice, and the length of the slice. Additionally, the slice has a reference to s (?), so s cannot be mutated while slice is still in use.

TIP

All string literals are slices!

let s = "abc"; // It's a slice!

A reference to a string is treated as a string slice.

Last Updated: 11/28/2022, 9:27:43 AM