7

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

mxxk
  • 9,514
  • 5
  • 38
  • 46
  • 1
    Might be related: https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules#comment78519904_28552082 – Finomnis Jun 03 '22 at 11:58
  • @Finomnis Pattern matching is unrelated to receiver resolution. I think the issue here is indeed a consequence of the mentioned "match ergonomics" RFC, albeit one that I wouldn't have expected either. (I personally strongly dislike match ergonomics, and judging by the questions we get about them here on Stack Overflow, they have caused a lot of confusion, while their goal was to make Rust _easier_ to use.) – Sven Marnach Jun 03 '22 at 14:29

1 Answers1

2

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.
EvilTak
  • 7,091
  • 27
  • 36
  • This is definitely not how I'd read the RFC. First, the code would not compile without match ergonomics, so I don't think you can claim they don't apply. And second, the RFC explicitly states there is no way back once we switched to a `ref` binding mode, which should be done on the outermost tuple pattern. – Sven Marnach Jun 04 '22 at 09:51
  • @SvenMarnach My wording could have been better - by "match ergonomics doesn't apply" I meant the binding mode for `key2` remains unchanged in the second case. This is because the inner pattern in question `&key2` is a _reference_ pattern, and the match ergonomics RFC, besides the auto deref, only changes the default binding mode for _non-reference_ patterns. We never actually switch to the `ref` binding mode in the second case, because we never match a reference with a non-reference pattern (besides the outermost reference of course, which is dereferenced). – EvilTak Jun 05 '22 at 03:17
  • I agree that this is what appears to be happening, but I still find it impossible to interpret the RFC wording this way. I don't even think the wording is ambiguous; it clearly specifies a different behaviour. – Sven Marnach Jun 05 '22 at 17:50