309: The Never Type (!) in Error Handling
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
! (never type) as the return type of functions that never return! coerces to any type, enabling use in any expression contextstd::convert::Infallible to mark operations that cannot fail! enables unwrap_infallible() — extraction without panicCode 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
! is a first-class type that participates in type checking; OCaml's raise works by being polymorphic rather than having a special type.Result<T, Infallible> proves at the type level that a conversion cannot fail — OCaml has no equivalent guarantee.impl From<Infallible> for Any enables writing generic code that handles ! errors uniformly without special cases.! 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");
}
}#[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
! in a match arm to prove exhaustiveness: a match on a Result<T, Infallible> that handles only Ok.From<Infallible> for MyError to demonstrate that infallible results can be unified with fallible ones in generic code.NeverFails<T> type alias for Result<T, Infallible> and demonstrate that unwrap() on it is always safe.