Tuple destructuring magic? &(T, T) vs (&T, &T)

fn main() {
    let v = &vec![("1".to_string(), "2".to_string()), ("1".to_string(), "2".to_string())];
    for (a,b) in v {
        let z: () = (a, b);
    }
    for c in v {
        let z: () = c;
    }
}
   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:4:21
  |
4 |         let z: () = (a, b);
  |                --   ^^^^^^ expected `()`, found tuple
  |                |
  |                expected due to this
  |
  = note: expected unit type `()`
                 found tuple `(&String, &String)`

error[E0308]: mismatched types
 --> src/main.rs:7:21
  |
7 |         let z: () = c;
  |                --   ^ expected `()`, found `&(String, String)`
  |                |
  |                expected due to this
  |
  = note: expected unit type `()`
             found reference `&(String, String)`

error: aborting due to 2 previous errors

How come when destructured, the tuple becomes (&String, &String) ?

It's called "match ergonomics". Instead of explaining it myself, let me link some Google search result :wink:

1 Like

This is the result of match ergonomics.

Not trying to be aggressive but it seems that match ergonomics would be.
c -> &(String, String) and thus to extract the value behind the reference &c -> (String, String)
I get that.

but how come
(a, b) which is c &(String, String) being destructured equals (&String, &String)

Basically how come &(String, String) == (&String, &String) ?

Sorry if I am too dumb to understand this. Bear with me

Perhaps try to read the article I linked from the beginning, (i. e. including the part above what I linked to), it sets it up quite well. You kind-of need to understand &-patterns and ref patterns to really understand what's happening and why.

One important insight is that without match ergonomics, your code wouldn't compile at all since you're matching a reference against a pattern for a tuple. Instead of a compilation error, the pattern (a, b) in this case gets a new meaning and "desugars" to &(ref a, ref b). There's no logical reason behind why it should do so, it's just an extra rule that's often convenient in practice.

3 Likes

I am back ...was testing in playground and before seeing your solution I tried:

    let v = &vec![("1".to_string(), "2".to_string()), ("1".to_string(), "2".to_string())];
    for (ref a, ref b) in v {
        let c:() = (a, b);
    }
 
 |
4 |         let c:() = (a, b);
  |               --   ^^^^^^ expected `()`, found tuple
  |               |
  |               expected due to this
  |
  = note: expected unit type `()`
                 found tuple `(&String, &String)`

Thank you. The fact that you mentioned ref put me on the right track. So It is thanks to you and you got my vote!

One way to think of the match ergonomics here is by analogy to for loops with containers.

If you for x in vec {, then x is a T.
If you for x in &vec {, then x is a &T.
If you for x in &mut vec {, then x is a &mut T.

If you match (a, b) { (x, y) =>, then x, y are T, U.
If you match &(a, b) { (x, y) =>, then x, y are &T, &U.
If you match &mut (a, b) { (x, y) =>, then x, y are &mut T, &mut U.

5 Likes

From my testing, it seems that you are closer to the answer @scottmcm.

ref mechanism is useful when destructuring values and not references.

    let tup = ( 1u8, 1u8 );
    match tup {
         (ref a, b) => {
            let _s:() =  (a, b);
        }
        _ => unreachable!(),
    }
  
 = note: found tuple `(&u8, u8)`

Destructuring of a reference of a tuple (or a struct etc..) results in forwarding the reference to its collection of values.

    let tup = ( 1u8, 1u8 );
    match &tup {
         (a, b) => {
            let _s:() = (a, b);
        }
        _ => unreachable!(),
    }
   = note: found tuple `(&u8, &u8)`

As demonstrated by the question, it's also useful when destructuring references, simply because it's clearer. Pattern matching "ergonomics" is a misfeature in my opinion. I suggest turning on the pattern_type_mismatch lint so that references stay references and values stay values.

2 Likes

Agree. Thanks for your comment @H2CO3. Will check the link.

Edit: The verbose style is definitely clearer.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.