mirror of
https://github.com/leonardomso/rust-skills.git
synced 2026-09-14 19:33:21 +08:00
0016d5cfb2
Includes rules for: - Ownership and borrowing patterns - Error handling with thiserror/anyhow - Memory management and allocation - API design following Rust guidelines - Async/Tokio patterns - Performance optimization - Naming conventions - Type safety - Testing strategies - Documentation standards - Project structure - Linting configuration - Common anti-patterns to avoid
2.9 KiB
2.9 KiB
type-never-diverge
Use
!(never type) for functions that never return
Why It Matters
The never type ! indicates a function will never return normally—it either loops forever, panics, or exits the process. This helps the compiler understand control flow and enables ! to coerce to any type, making it useful in match arms and expressions.
Bad
// Return type doesn't indicate non-returning
fn infinite_loop() {
loop {
process_events();
}
// Implicit () return type, but never returns
}
// Using Option when it always panics
fn unreachable_code() -> Option<()> {
panic!("This should never be called");
}
Good
// ! indicates function never returns
fn infinite_loop() -> ! {
loop {
process_events();
}
}
fn abort_with_error(msg: &str) -> ! {
eprintln!("Fatal error: {}", msg);
std::process::exit(1);
}
fn panic_handler() -> ! {
panic!("Unexpected state");
}
Coercion to Any Type
// ! coerces to any type
fn get_value(opt: Option<i32>) -> i32 {
match opt {
Some(v) => v,
None => panic!("No value"), // panic! returns !, coerces to i32
}
}
// Useful in Result handling
fn must_get_config() -> Config {
match load_config() {
Ok(c) => c,
Err(e) => {
log_error(&e);
std::process::exit(1) // Returns !, coerces to Config
}
}
}
Standard Library Examples
// std::process::exit
pub fn exit(code: i32) -> !
// panic! macro
// Expands to an expression of type !
// std::hint::unreachable_unchecked
pub unsafe fn unreachable_unchecked() -> !
// loop {} with no break
fn forever() -> ! {
loop {}
}
In Match Expressions
enum State {
Running,
Stopped,
Error,
}
fn get_status(state: &State) -> &str {
match state {
State::Running => "running",
State::Stopped => "stopped",
State::Error => unreachable!(), // ! coerces to &str
}
}
// With Result
fn process(r: Result<Data, Error>) -> Data {
match r {
Ok(d) => d,
Err(e) => panic!("Unexpected error: {}", e), // ! coerces to Data
}
}
Diverging Closures
// Closures that never return
let handler: fn() -> ! = || {
panic!("Handler called");
};
// In thread spawn
std::thread::spawn(|| -> ! {
loop {
process_work();
}
});
Current Limitations (Nightly)
// Full ! type is nightly
#![feature(never_type)]
// Can use ! as type parameter
type NeverResult = Result<(), !>; // Can never be Err
// On stable, use std::convert::Infallible
type StableNeverResult = Result<(), std::convert::Infallible>;
See Also
- err-result-over-panic - When to panic vs return Result
- type-result-fallible - Result for errors
- opt-cold-unlikely - Marking unlikely paths