Search code examples
rustreferencedereference

Rust Refs & Derefs


Would like to check if my understandings are correct:

case 1

fn main() {

    let x = 5;
    let y = x;

    assert_eq!(5, y);        
    println!("y = {}", y);
}

Q1) {assert_eq!(5, &y)} would fail ("no implementation for {integer} == &{integer}"). I think y clones the value of 5 in the stack? Because it is primitive?

case 2

fn main() {

    let x = 5;
    let y = &x;

    assert_eq!(5, *y);         
    println!("y = {}", y);
}

Q2) this is how I infer case 1, as seemingly this is how to force a ref instead of cloning in case of primitives? In this case y contains the address (or stack position) of x?

Q3) if x is a string allocated from heap, as in:

fn main() {

    let x = String::from("Five");
    let y = &x;

    assert_eq!("Five", y);         
    println!("y = {}", y);
}

{assert_eq!(5, y)} would suffice (vs *y) only because of implicit deref of compiler?

case 3

fn main() {

    let x = 5;
    let &y = &x;

    assert_eq!(5, y);          
    println!("y = {}", y);
}

Similar to case 1 but not a clone? What is y? A ref to ref x? Compiler says y is an integer (presumably '5'), and this because the compiler will auto resolve daisy-chained refs I believe?

case 4

fn main() {

    let x = 5;
    let &y = &x;
    let &z = &&x;

    assert_eq!(5, *z); 
    println!("y = {}", z);
}

The above works but not with {assert_eq!(5, z)} - this is not the same as daisy-chaining refs?


Solution

  • Q1) {assert_eq!(5, &y)} would fail ("no implementation for {integer} == &{integer}"). I think y clones the value of 5 in the stack? Because it is primitive?

    It's because it is Copy, hence values are, well, copied. Rather than moved.

    Q2) this is how I infer case 1, as seemingly this is how to force a ref instead of cloning in case of primitives?

    You're still forcing a copy when you dereference y.

    In this case y contains the address (or stack position) of x?

    Sure.

    Q3) if x is a string allocated from heap, {assert_eq!(5, y)} would suffice (vs *y) only because of implicit deref of compiler?

    Well first it'd never work because the types can not match, 5 is never a valid string, or reference to string, or anything even remotely close to a string.

    Second, implicit dereference only comes into play when calling methods, although as tadman notes a single * might go through multiple levels of dereference.

    (case 4) Similar to case 1 but not a clone? What is y? A ref to ref x? Compiler says y is an integer (presumably '5'), and this because the compiler will auto resolve daisy-chained refs I believe?

    There's no daisy chain. The left hand of let is a pattern. By design, patterns try to reverse an existing operation using the same syntax.

    let &y = ... is a dereference of the value, as if you'd written let y = *... (except more limited, it only works with references). Not to be confused with ref (which is essentially the reverse of *).

    Therefore this does the exact same thing as case 1 in a roundabout way, including the copy. y is an integer, of the same type as x.

    (case 5) The above works but not with {assert_eq!(5, z)} - this is not the same as daisy-chaining refs?

    If you apply the rule above you can see that:

    • x: i32
    • y: i32 because let &y = &x; is a complicated way of writing let y = x; aka a copy
    • z: &i32 because in the same way let &z = &&x; is a complicated way of writing let z = &x, aka a reference to x

    Which is why you have to dereference z for the type to match with 5.