ExamplesBy LevelBy TopicLearning Paths
309 Advanced

309: The Never Type (!) in Error Handling

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "309: The Never Type (!) in Error Handling" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. Some functions never return — they loop forever, panic, or call `std::process::exit()`. Key difference from OCaml: 1. **Type system integration**: Rust's `!` is a first

Tutorial

The Problem

Some functions never return — they loop forever, panic, or call std::process::exit(). Rust's ! (never type) represents the return type of such functions. It coerces to any type, enabling panic!() in match arms that need any type, and it is the error type for infallible operations. std::convert::Infallible is the named equivalent for Result<T, !> — a result that can only ever be Ok. Understanding ! clarifies why certain patterns compile.

🎯 Learning Outcomes

  • • Understand ! (never type) as the return type of functions that never return
  • • Recognize that ! coerces to any type, enabling use in any expression context
  • • Use std::convert::Infallible to mark operations that cannot fail
  • • Understand how ! enables unwrap_infallible() — extraction without panic
  • Code Example

    #![allow(clippy::all)]
    //! # The ! (never) Type in Error Handling
    //!
    //! `!` is the never/bottom type for diverging functions.
    
    use std::convert::Infallible;
    
    /// Function that never returns
    pub fn crash(msg: &str) -> ! {
        panic!("{}", msg)
    }
    
    /// ! coerces to any type
    pub fn parse_or_crash(s: &str) -> i32 {
        s.parse::<i32>()
            .unwrap_or_else(|e| crash(&format!("fatal: {}", e)))
    }
    
    /// Infallible conversion - can only be Ok
    pub fn to_uppercase(s: &str) -> Result<String, Infallible> {
        Ok(s.to_uppercase())
    }
    
    /// Extension trait for unwrap_infallible
    pub trait UnwrapInfallible<T> {
        fn unwrap_infallible(self) -> T;
    }
    
    impl<T> UnwrapInfallible<T> for Result<T, Infallible> {
        fn unwrap_infallible(self) -> T {
            match self {
                Ok(v) => v,
            }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_infallible_is_ok() {
            let r: Result<i32, Infallible> = Ok(42);
            assert!(r.is_ok());
            assert_eq!(r.unwrap(), 42);
        }
    
        #[test]
        fn test_to_uppercase() {
            let r = to_uppercase("rust");
            assert_eq!(r.unwrap(), "RUST");
        }
    
        #[test]
        fn test_unwrap_infallible() {
            let r: Result<String, Infallible> = Ok("hello".to_string());
            assert_eq!(r.unwrap_infallible(), "hello");
        }
    
        #[test]
        fn test_parse_or_crash() {
            assert_eq!(parse_or_crash("100"), 100);
        }
    
        #[test]
        #[should_panic]
        fn test_crash_panics() {
            crash("intentional");
        }
    }

    Key Differences

  • Type system integration: Rust's ! is a first-class type that participates in type checking; OCaml's raise works by being polymorphic rather than having a special type.
  • Infallible conversions: Result<T, Infallible> proves at the type level that a conversion cannot fail — OCaml has no equivalent guarantee.
  • From impl: impl From<Infallible> for Any enables writing generic code that handles ! errors uniformly without special cases.
  • Stabilization: The ! type was a long-stabilization feature in Rust — older code uses std::convert::Infallible (an alias for !) for compatibility.
  • OCaml Approach

    OCaml's 'a bottom type (type 'a t = T of 'a) doesn't have a direct equivalent. The raise function has type exn -> 'a — it is polymorphic because it never returns, so it can be used anywhere any type is expected:

    (* raise has type exn -> 'a — can appear in any context *)
    let parse_or_crash s =
      match int_of_string_opt s with
      | Some n -> n
      | None -> raise (Invalid_argument "not a number")
    

    OCaml lacks Infallible — there is no way to statically prove a result error case is unreachable.

    Full Source

    #![allow(clippy::all)]
    //! # The ! (never) Type in Error Handling
    //!
    //! `!` is the never/bottom type for diverging functions.
    
    use std::convert::Infallible;
    
    /// Function that never returns
    pub fn crash(msg: &str) -> ! {
        panic!("{}", msg)
    }
    
    /// ! coerces to any type
    pub fn parse_or_crash(s: &str) -> i32 {
        s.parse::<i32>()
            .unwrap_or_else(|e| crash(&format!("fatal: {}", e)))
    }
    
    /// Infallible conversion - can only be Ok
    pub fn to_uppercase(s: &str) -> Result<String, Infallible> {
        Ok(s.to_uppercase())
    }
    
    /// Extension trait for unwrap_infallible
    pub trait UnwrapInfallible<T> {
        fn unwrap_infallible(self) -> T;
    }
    
    impl<T> UnwrapInfallible<T> for Result<T, Infallible> {
        fn unwrap_infallible(self) -> T {
            match self {
                Ok(v) => v,
            }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_infallible_is_ok() {
            let r: Result<i32, Infallible> = Ok(42);
            assert!(r.is_ok());
            assert_eq!(r.unwrap(), 42);
        }
    
        #[test]
        fn test_to_uppercase() {
            let r = to_uppercase("rust");
            assert_eq!(r.unwrap(), "RUST");
        }
    
        #[test]
        fn test_unwrap_infallible() {
            let r: Result<String, Infallible> = Ok("hello".to_string());
            assert_eq!(r.unwrap_infallible(), "hello");
        }
    
        #[test]
        fn test_parse_or_crash() {
            assert_eq!(parse_or_crash("100"), 100);
        }
    
        #[test]
        #[should_panic]
        fn test_crash_panics() {
            crash("intentional");
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_infallible_is_ok() {
            let r: Result<i32, Infallible> = Ok(42);
            assert!(r.is_ok());
            assert_eq!(r.unwrap(), 42);
        }
    
        #[test]
        fn test_to_uppercase() {
            let r = to_uppercase("rust");
            assert_eq!(r.unwrap(), "RUST");
        }
    
        #[test]
        fn test_unwrap_infallible() {
            let r: Result<String, Infallible> = Ok("hello".to_string());
            assert_eq!(r.unwrap_infallible(), "hello");
        }
    
        #[test]
        fn test_parse_or_crash() {
            assert_eq!(parse_or_crash("100"), 100);
        }
    
        #[test]
        #[should_panic]
        fn test_crash_panics() {
            crash("intentional");
        }
    }

    Deep Comparison

    never-type-errors

    See README.md for details.

    Exercises

  • Write a function that uses ! in a match arm to prove exhaustiveness: a match on a Result<T, Infallible> that handles only Ok.
  • Implement From<Infallible> for MyError to demonstrate that infallible results can be unified with fallible ones in generic code.
  • Create a NeverFails<T> type alias for Result<T, Infallible> and demonstrate that unwrap() on it is always safe.
  • Open Source Repos