How to match `T` and `&T` in the same arm?

I'm trying to understand why this code shouldn't compile:

enum Foo<'a, T> {
    A,
    B(T),
    C(&'a T),
}

fn main() {
    let foo = Foo::<'static, ()>::A;
    
    match foo {
        Foo::A => {},
        Foo::B(ref t) | Foo::C(t) => todo!(),
    }
}

isn't t bound to an &T in both cases? This more verbose version works just fine:

fn main() {
    let foo = Foo::<'static, ()>::A;
    
    let t = match foo {
        Foo::A => return,
        Foo::B(ref t) => t,
        Foo::C(t) => t,
    };
    
    todo!()
}

Link to a playground.

2 Likes

t has to be bound the same way in all alternatives joined with |. I'm not sure if there is a "real" reason for this or if it's a consequence of how pattern ergonomics works, or something else, but you can work around it (at least in this case) by matching

Foo::B(ref t) | Foo::C(&ref t) => ...
//                     ^^^^^

It could use a better error message, but the reason is because they have different lifetimes. When you match it separately, that gives it a chance for variance to coerce the lifetime shorter. You can force C to reborrow with &(ref t).

7 Likes

I filed a diagnostic issue:

3 Likes