When to ref or &

Hi,

Something I have been wondering a bit is the rules for when to use ref instead of &

Take this example

struct Foo {
    bar: i32,
}

fn main() {
    let mut t = vec![Foo { bar: 0 }, Foo { bar: 1 }];
    t.retain(|&v| v.bar != 1);
}

The compiler will tell me to use ref` here instead.

But if I add #[derive(Copy, Clone)] on the struct like this:

#[derive(Copy, Clone)]
struct Foo {
    bar: i32,
}

fn main() {
    let mut t = vec![Foo { bar: 0 }, Foo { bar: 1 }];
    t.retain(|&v| v.bar != 1);
}

It all works fine. So I wonder when is the ref keyword supposed to be used over & in these cases the compiler is happy to help out but I would like to know the rules of it.

Cheers!

2 Likes

ref in a binding is the same as & in an expression. In other words, these two are equivalent:

let ref a=2;
let a=&2;

Both variables will have type &i32. Both exist, because sometimes you can only specify things in a binding (e.g. when pattern matching) and other times you can only specify things in an expression (e.g. when calling a function). If both are an option, prefer &.

Likewise, & in a binding is the same as * in an expression, which makes these equivalent:

let r=&1;
let &a=r;
let a=*r;

Both variables will have type i32. Again, both exist because you can't write the other form everywhere.

In your particular example the closure will be called with an argument of type &Foo. By specifying &v you are automatically dereferencing the argument to get a Foo. Since Foo is not Copy in the first example, the compiler doesn't let you dereference like that. You probably just want to specify |v| which will receive an argument of type &Foo.

10 Likes

In this case, the advice to use ref is misleading. What the compiler wants you to do is

t.retain(|& ref v| v.bar != 1)

which is the same as

t.retain(|v| v.bar != 1)

which you should obviously prefer :slight_smile:

I guess one could open an issue against rustc to suggest removing & before suggesting adding ref.

3 Likes

Alright. Thanks :slight_smile:

Sorry for digging up an old conversation, but I'm confused, how is t.retain(|& ref v| v.bar != 1) the same as t.retain(|v| v.bar != 1)? Shouldn't it be more something like this?

t.retain(|&&v| v.bar != 1)

Hi,

There is another thread about it here Ref keyword versus & with a blog post at the end Ref patterns, destructuring, and invisible borrows | by Robert Grosse | Medium hopefully this will explain it.

Cheers!