Introducing cain!, a proc macro that lets you have branches with differently typed arms

93 points frxstrem

Pretty simple problem, you have a match with multiple arms of different types, and you want to do the same thing to the result of both:

fn foo_or_bar() -> Result<Foo, Bar> { .. }


let x = match foo_or_bar() {
    Ok(foo) => foo,
    Err(bar) => bar,
};

let y = if should_frobnify {
    x.frobnify()
} else {
    x
};

println!("y = {y:?}");

But you get an error!

error[E0308]: `match` arms have incompatible types

You could, of course, solve this by using dyn or enum, but I think that shouldn't be necessary, so for fun and as a challenge I wrote a proc macro cain! that rewrites code such as the one above, into code that removes the incompatible arm types (as long as the last statement has the same type for all code paths):

match foo_or_bar() {
    Ok(foo) => {
        let x = foo;
        
        if should_frobnify {
            let y = x.frobnify();
            println!("y = {y:?}");
        } else {
            let y = x;
            println!("y = {y:?}");
        }
    },
    Err(bar) => {
        let x = bar;
        
        if should_frobnify {
            let y = x.frobnify();
            println!("y = {y:?}");
        } else {
            let y = x;
            println!("y = {y:?}");
        }
    },
}

So now you can cleanly write branches with incompatible types, without angering the compiler!