What's the deal with `&*`?

Hi,

from time to time I see &*foo, and I have never really understood it. I just saw it again in one of the threads here in URLO, so I finally decided to ask what's that about.

For example I just saw it in @alice's post here.

When should one use &*foo instead of just foo?

2 Likes

The * operator can be used to dereference any “pointer” type, meaning anything that implements the Deref and/or DerefMut traits.

For example, String, &mut str, Box<str>, MutexGuard<str>, and Rc<str> all point to a str slice. (They all implement Deref<str>.) So if you have a variable s with any of those types, then &*s will borrow an &str reference to that slice.

In many cases, deref coercion allows you to leave out the * and just write &s.

In alice's post, &* was used to explicitly convert from &mut T to &T.

6 Likes

I think I get it, but why is a conversion from &mut T to &T necessary? This compiles just fine:

fn foo(x: &i32) {
    print!("x = {}", *x);
}

fn main() {
    let mut x = 42;
    foo(&mut x);
}

There are some places where the coercion does not trigger, for example

Also all those type coercions are disabled within the generic context. &T and &*T may implements same traits with different implementation, and we don't allow coercions implicitly change our code's behavior.

Wait, but the book explains deref coercion with generics?

Rust does deref coercion when it finds types and trait implementations in three cases:

  • From &T to &U when T: Deref<Target=U>
  • From &mut T to &mut U when T: DerefMut<Target=U>
  • From &mut T to &U when T: Deref<Target=U>

It's just a way to explain when it happens. When type inference doesn't know both the source and target of a deref coercion for certain, it will not coerce.

1 Like

I think it's slowly clicking for me, but I still do not understand this:

If costumer is an &mut T, then writing &costumer gives you an &&mut T. If you wanted an &T instead, you write &*customer. This makes sense because *customer on a &mut T gives an T, so adding an & on that gives a &T.

Sure, sometimes this conversion happens automatically, but not always, and it is possible to explicitly write the conversion with &*.

3 Likes

Alright, I understand now. Thanks for your help!

It is necessary if you want to have multiple non-unique references down the line:

fn print(x: &u64) {
    println!("{}", x);
}

fn do_it(x: &mut u64) {
    *x += 1;
    let y = &*x;
    let z = &*x;
    print(y);
    print(z);
}

fn main() {
    let mut x = 3;
    do_it(&mut x);
}
1 Like