4

The Condvar docs shows an example that includes the following:

let pair = Arc::new((Mutex::new(false), Condvar::new()));
// <snipped for brevity>
let &(ref lock, ref cvar) = &*pair;

I'm wondering what the advantage might be to including & on both sides of this assignment. In other words, why not just write:

let (ref lock, ref cvar) = *pair;

Both versions compile; is there any semantic difference? And if not, is there any reason to prefer the syntax that appears in the example?

In general, I'm still struggling to understand when/where &* should idiomatically appear in Rust code. My instinct is to "see" (read) this character combination as a no-op, though I understand that because of Rust's Deref semantics this isn't the case.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bosh
  • 8,138
  • 11
  • 51
  • 77
  • Let me add that I've found https://github.com/rust-lang/rfcs/blob/master/text/0241-deref-conversions.md most helpful in understanding the design intention/thinking behind Deref behavior. – Bosh Jun 16 '16 at 01:30

1 Answers1

3

The use of & in this case does not make difference because the LHS is a destructuring pattern. In my opinion, this example should not use &. However, in other cases there is a important difference: * moves the deferred value:

let p = *pair; // *Deref::deref(pair)
// pair cannot be used anymore because the deferred value was moved

let p = &*pair; // &*Deref::deref(pair)
// p: &(Mutex::new(false), Condvar::new())
// p and pair can be used

I'm still struggling to understand when/where &* should idiomatically appear in Rust code.

In general, use &* when a reference to the deferred value is needed, like in a function call:

fn sum(a: &u32, b: &u32) -> u32 {
   a + b
}

fn main() {
    let x = Box::new(10);
    // println!("{:?}", sum(&1, x)); // do not work x is Box<u32>
    // println!("{:?}", sum(&1, *x)); // do not work *x is u32
    println!("{:?}", sum(&1, &*x)); // ok &*x is &u32
}

My instinct is to "see" (read) this character combination as a no-op, though I understand that because of Rust's Deref semantics this isn't the case.

Considering that The Book says "Deref is useful for writing custom pointer types", I like to read &* as "a reference to the value that this (smart) pointer points at".

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
malbarbo
  • 10,717
  • 1
  • 42
  • 57
  • This is great! Can you clarify for `// *Deref::deref(pair)` why the `*` *and* `Deref` both appear in your explanation? Shouldn't just one be there? – Bosh Jun 15 '16 at 18:55
  • The compiler translates `*pair` to `*Deref::deref(pair)` and `&*pair` to `&*Deref::deref(pair)`, considering that `Deref::deref` returns `&T`, then `*pair` produces `T` and `&*pair` produces `&T`. – malbarbo Jun 15 '16 at 19:04
  • Silly question, but if `*pair` expands to `*Deref::deref(pair)`, then why doesn't this in turn expand to `*Deref::refer(*Deref::deref(pair))`? – Bosh Jun 15 '16 at 23:52
  • Also is it at all possible to explain https://doc.rust-lang.org/src/alloc/up/src/liballoc/boxed.rs.html#459 — how is this not recursive? – Bosh Jun 16 '16 at 02:01
  • I tried to find an official description of how the translation is done but did not succeed. But I suppose that the expansion is done one time for each `*` and that the result is not expanded again. If I find an official description, I will update the answer. – malbarbo Jun 16 '16 at 21:39