Ref and & for function arguments


#1

How ref works in functions arguments?

fn test(ref a: i32) {}

at first I think that it is the same as fn test(a: &i32) {},
but

fn test(ref a: i32) {
    let b: &i32 = a;
    println!("0) a: {}, addr of a {:?}", *b, b as *const i32);
}

fn test2(a: &i32) {
    println!("1) a: {}, addr of a {:?}", *a, a as *const i32);
}

fn main() {
    let x: i32 = 5;
    println!("addr of x {:?}", &x as *const i32);
    test(x);
    test2(&x);
}

for reference address of argument in test2 and x are the same,
but address of argument of test and x are different.


#2

When you pass arguments by value (the first test) the arguments are typically copied, so the address you are seeing is the address of a new copy of the input parameter. Whereas if you pass in a reference then the address you see will be that of the original input.


#3

It is essentially the same as;

fn test(a: i32) {
    let ref a = a;
}

It becomes more useful with a more complex pattern.

fn t(&mut (ref mut a, ref mut b) : &mut (i32, i32));

#4

Let’s suppose we have variable b. When we had replaced declaration of b with ref a, we have created reference to now unnamed variable b.

That is
fn test(ref a: i32) {}
is equivalent to
fn test(b: i32) { let a = &b; }


#5

here is a small example to show the differences

// Crate a non copy type to make the move error visible
#[derive(Debug)]
struct NoCopy;

#[derive(Debug)]
struct Foo(NoCopy);

// this takes Foo by ref but it tries to take ownership of the inner value
// with we can't do since Foo is borrowed and the value can't be copied.
// fn test(&Foo(val): &Foo) {}

// here we take a ref to Foo and take a ref to it's inner value
fn test_ref(&Foo(ref val): &Foo) {
    println!("val: {:?}", val)
}

fn main() {
    let foo = &Foo(NoCopy);
    test_ref(foo);
}

edit: btw we don’t need to make the destruction in the function header we can also do it in the body like this:

fn test_ref(foo: &Foo) {
    let &Foo(ref val) = foo;
    println!("val: {:?}", val)
}

#6
let ref x = 1;

is identical to:

let x = &1;

And:

let &x = &1;

is identical to:

let x = 1

So in patterns (let on the left of =, function arguments on the left of :, and match arms) ref takes value by reference, and & matches a value that is already a reference (also called destructuring assignment).