问题
I'm looking at the Condvar example and am curious how the tuples pair and pair2 are destructured:
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
// ...
thread::spawn(move|| {
let &(ref lock, ref cvar) = &*pair2;
// ...
}
Removing the & from pair2:
let &(ref lock, ref cvar) = *pair2;
gives a compiler error as I expected:
11 | let &(ref lock, ref cvar) = *pair2;
| ^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference
|
= note: expected type `(std::sync::Mutex<bool>, std::sync::Condvar)`
found type `&_`
However, it seems to compile and run fine if the & around the tuple is removed:
let (ref lock, ref cvar) = &*pair2;
or if both of the &'s are removed:
let (ref lock, ref cvar) = *pair2;
or even with
let (lock, cvar) = &*pair2;
Is the compiler is helping us in the latter cases?
回答1:
The compiler is helping us using match ergonomics. Match ergonomics applies to tuple destructuring and the match expression; we'll look at the case of the match expression first.
Match ergonomics simplifies how Rust binds variables when a reference is matched to a non-reference pattern:
let x = Some(42);
let x_ref = &x;
match x_ref { // <-- reference match expression: `x_ref`
Some(a) => {}, // <-- non-reference pattern: `Some(a)`
None => {},
}
Older version of the Rust compiler didn't allow this. Instead, one had to either specify references (&) in the pattern:
// Old Rust
match x_ref {
&Some(a) => {},
&None => {},
}
or dereference before matching:
// Old Rust
match *x_ref {
Some(a) => {},
None => {},
}
Notice that a owns the Option's inner value, which is problematic for non-Copy types. To avoid this, one also had to borrow the inner value. This is done by binding a as a reference using the ref keyword:
// Old Rust
let x = Some(Box::new(42));
let x_ref = &x;
match x_ref {
&Some(ref a) => {},
&None => {},
}
or
// Old Rust
match *x_ref {
Some(ref a) => {},
None => {},
}
Rust's new match ergonomics allow a simplified version:
// New Rust
match x_ref {
Some(a) => {
// x_ref is automatically dereferenced
// a is automatically bound as a reference
},
None => {},
}
for both Copy and non-Copy types.
Applying this to tuple destructuring,
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let (lock, cvar) = &*pair;
// ^^^^^^ reference match expression
// ^^^^^^^^^^^^ non-reference pattern
*pairis a tuple&*pairis a reference to a tuple- Both
lockandcvarare bound as references
Also see these Stack Overflow posts on:
- the matching and the ref keyword
- pattern matching on a reference or a dereferenced value
- binding during pattern matching
来源:https://stackoverflow.com/questions/57128842/how-are-tuples-destructured-into-references