Skip to content

Latest commit

 

History

History
118 lines (89 loc) · 2.64 KB

06_references.md

File metadata and controls

118 lines (89 loc) · 2.64 KB

References

Reference to a value is taken by the & operator. This creates a thin pointer to the data. Taking a reference to a value is called borrowing.

fn main() {
    let s = "kek".to_string();
    println!("Length is {}", string_length(&s));
}

fn string_length(s: &String) -> usize {
    s.len()
}

Reference to a fat pointer to a value stored on the heap:

Reference to a fat pointer

References are immutable by default. Only mutable variables can be borrowed as mutable. Use &mut to make a reference mutable:

fn main() {
    let mut s = "top".to_string();
    append_kek(&mut s); // s is now topkek
}

fn append_kek(s: &mut String) {
    s.push_str("kek");
}

References must obey the following rules:

  • there can be any number of immutable references
  • there can be only one mutable reference
  • when a mutable reference exists, no immutable reference can exist

Scope

Same as variables, references are valid until the end of their scope. Immutable references are no longer valid after a mutable borrow:

fn main() {
    let mut number = 5;
    let x = &number;     // valid
    let y = &number;     // valid
    println!("Numbers: {}, {}", x, y);

    let z = &mut number; // x and y no longer valid!
    println!("Number: {}", z);
}

References whose values were created inside a function cannot be returned from within the function. Functions can only return created primitives and fat pointers:

// OK, value copied
fn gimme_primitive() -> i32 {
    1337
}

// OK, ownership moved
fn gimme_pointer() -> String {
    "yep".to_string()
}

// Error!
fn bad_function() -> &String {
    let s = "nope".to_string();
    &s // compilation error!
}  // s no longer valid here, cannot return from the function

Dereferencing

Mutating referenced values is done by dereferencing using the * operator:

fn main() {
    let mut n = 10;
    to_five(&mut n); // n is now 5
}

fn to_five(x: &mut i32) {
    *x = 5; // changes the value where the reference points at to 5
}

Note that the reference must be a &mut.

Deref coercion

A compiler feature called Deref coercion automatically dereferences types implementing the Deref trait if the supplied type to a function call doesn't match the expected type.

The supplied type is being dereferenced until the suitable type is found:

fn gib_num(x: &i32) {
    // ...
}

fn main() {
    let x = Box::new(5);
    gib_num(&*x);   // is the explicit form
    gib_num(&x);    // turns into &*x
    gib_num(&&&x);  // turns into &*&*&*x
}

This feature is most commonly used by fat pointers and slices.