What's the right syntax for an infallible reference?

The compiler does not recognise Ok(&'r !) as an infallible variant. I'm starting with the assumption that the error is in my own understanding and/or syntax and am turning up a blank on solving this.

#![feature(never_type)]
#![feature(try_trait_v2)]

use std::ops::FromResidual;

enum MyResult<'r, T, E> {
    Ok(&'r T),
    Err(&'r E),
}

// This should compile - instead it gives error[E0004]: non-exhaustive patterns: `lifetimes::MyResult::Ok(_)` not covered
impl<'r, T, E> FromResidual<MyResult<'r, !, E>> for MyResult<'r, T, E> {
    fn from_residual(residual: MyResult<'r, !, E>) -> Self {
        match residual {
            MyResult::Err(_) => todo!(),
        }
    }
}

// Longhand alternative ...
enum MyResidual<'r, E> {
    Ok(&'r !),
    Err(&'r E),
}

// ... also refuses to compile with error[E0004]: non-exhaustive patterns: `MyResidual::Ok(_)` not covered
impl<'r, T, E> FromResidual<MyResidual<'r, E>> for MyResult<'r, T, E> {
    fn from_residual(residual: MyResidual<'r, E>) -> Self {
        match residual {
            MyResidual::Err(_) => todo!(),
        }
    }
}

// For comparison this works fine ...
enum MyOwnedResidual<'r, E> {
    Ok(!),
    Err(&'r E),
}

// ... and compiles without requiring an Ok(_) pattern
impl<'r, T, E> FromResidual<MyOwnedResidual<'r, E>> for MyResult<'r, T, E> {
    fn from_residual(residual: MyOwnedResidual<'r, E>) -> Self {
        match residual {
            MyOwnedResidual::Err(_) => todo!(),
        }
    }
}

here's this example on playground

The same thing happens if using std::convert::Infallible rather than !

you need to exhaustively match your own enum, in doing so you destructure the Ok variant and bind a variable to a never type, which can then be eliminated as an uninhabited type, e.g. with match or coercion.

impl<'r, T, E> FromResidual<MyResult<'r, !, E>> for MyResult<'r, T, E> {
    fn from_residual(residual: MyResult<'r, !, E>) -> Self {
        match residual {
            MyResult::Err(_) => todo!(),
            MyResult::Ok(&never) => match never {}
            // also valid, because `!` coerce to arbitrary type:
            //MyResult::Ok(&never) => never,
        }
    }
}
1 Like

Whether or not references to uninhabited types are themselves uninhabited is an open question.

4 Likes

Thanks,
What's the advantage of that rather than simply

impl<'r, T, E> FromResidual<MyResult<'r, !, E>> for MyResult<'r, T, E> {
    fn from_residual(residual: MyResult<'r, !, E>) -> Self {
        match residual {
            MyResult::Err(_) => todo!(),
            MyResult::Ok(_) => unreachable!("&!")
        }
    }
}

The advantage is that the compiler checks that the code is actually unreachable; whereas if you write unreachable!(), there is no such check. unreachable!() should be used when you can't write the code in a way that the compiler can see the unreachability, but in this case you can — it’s just less obvious than the way you hoped for.

3 Likes