External Publication
Visit Post

Pre-RFC improved ergonomics for `!`

Rust Internals [Unofficial] May 19, 2026
Source

robofinch:

Yes, this is an absurd scenario with a useless type, but it would then be unsound to coerce UninhabitedErr<T, !> to a general Uninhabited<T, E> where E may be a non-ZST or inhabited type.

(I just realise I'd overread what might be a typo ... assuming you meant coercing to a general UninhabitedErr<T,E> ...)

Are you talking about something like a less-naive version of this (playground):

#![allow(unused_variables)]
#![feature(never_type)]

/// A wrapper around a result which is always OK. We rely on X being a ZST for
/// various optimisations and functionality. (See SAFETY, below, for details on the
/// consequences.)
///
/// ## SAFETY
/// - X must be a zero-sized type. We have no way to ensure that the compiler
///   will validate this, so the constructor and .map_err() are both `unsafe`.
pub struct Always<T, X>(Result<T, X>);

impl<T, X> Always<T, X> {
    /// ## SAFETY
    /// - X must be a zero-sized type. When calling `new` you must guarantee that
    ///   this is the case.
    pub unsafe fn new(t: T) -> Self {
        Self(Result::Ok(t))
    }

    pub fn map<F, U>(self, f: F) -> Always<U, X>
    where
        F: FnOnce(T) -> U,
    {
        Always(self.0.map(f))
    }

    /// Use map_err to change e.g. Always<String, !> to Always<String, Infallible>
    ///
    /// ## SAFETY
    /// - Z must be a zero-sized type. When calling `map_err` you must guarantee that
    ///   this is the case.
    pub unsafe fn map_err<Z>(self) -> Always<T, Z> {
        unsafe { Always(Ok(self.0.unwrap_unchecked())) }
    }
}

pub enum ZST {}

fn main() {
    let bang: Always<u32, !> = unsafe { Always::new(5) };
    let bang = bang.map(|x| x+1);
    let custom_zst: Always<u32, ZST> = unsafe { bang.map_err() };
}

If so ... I can see how any attempt to fake a map_err-like function would invalidate the safety of the type.

The best, safe, way to manage that kind of situation may well be to restrict the feature to only converting Try-types which have a suitable implementation of FromResidual. I can't see a way to implement Try and FromResidual safely for the above example (anyone?).

That would cover the "nested, no map / map is ugly" cases and leave the trait-bound case as "will be covered by the blanket impl From<!> for T at some point anyway, so don't try to overcomplicate things".

Discussion in the ATmosphere

Loading comments...