How does reference pattern appear to dereference twice?

Viewed 89

Can someone please explain, per the rules of match ergonomics (RFC 2005), how the use of a single & in a reference pattern appears to dereference the matched value twice?

Example

Suppose we have a map: HashMap<i32, bool>. Consider the expression map.iter().filter(|entry| ...), where the type of entry is &(&i32, &bool). Now, what if we pattern-match against entry in the following two ways?

map
    .iter()
    .filter(|entry| {
        let (key1, _) = entry;   // typeof(key1) -> &&i32
        let (&key2, _) = entry;  // typeof(key2) -> i32
    })

As far as I understand, both patterns match a reference (to a tuple) using a non-reference pattern, and therefore change the default binding mode to ref. But what has me stumped is how the type of key2 ends up as i32 and not &i32.

According to RFC 2005, here is an attempt to write desugared patterns of the above two:

let &(ref key1_desugared, _) = entry;    // typeof(key1_desugared) -> &&i32
let &(& ref key2_desugared, _) = entry;  // typeof(key2_desugared) -> &i32

Though the type of key1_desuraged ends up matching the type of key1, the type of key2_desugared is &i32.

What is the correct way to express the pattern key2 in desugared form?

Playground

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1cb8170b1221fb1dd221db885f8eed8d

1 Answers

The important thing to note here is that match ergonomics only changes the binding mode for non-reference patterns and not reference patterns. This is equivalent to the second case discussed in the question Match ergonomics and & pattern. Applying the explanation in the answer to that question:

  • (key1, _) is a non-reference pattern being matched to a reference, so entry is dereferenced to a (&i32, _). Since key1 is a non-reference pattern, it is now bound with ref mode (since the outer binding was dereferenced). As entry.0 is an &i32, key1 now becomes a reference to a &i32, or a &&i32.
  • (&key2, _) is a non-reference pattern, so entry is dereferenced to a (&i32, _). &key2 is then matched to a &i32 reference. Since &key2 is a reference pattern, match ergonomics does not apply. key2 is hence an i32.
Related