โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
use std::convert::{Infallible, TryFrom};
// From<u8> for u32 is infallible โ u8 always fits in u32
let n: u32 = u32::from(255u8); // never fails
// The blanket impl gives us TryFrom for free:
let r: Result<u32, Infallible> = u32::try_from(255u8);
// r is always Ok โ Infallible proves it
// Your own type: From gives you TryFrom automatically
struct Meters(f64);
impl From<f64> for Meters {
fn from(v: f64) -> Self { Meters(v) }
}
// Now Meters::try_from(1.5f64) works with Error = Infallible
// Generic code works with both:
fn convert<T, U: TryFrom<T, Error = Infallible>>(val: T) -> U {
U::try_from(val).unwrap() // safe โ Infallible proves no panic
}
| Concept | OCaml | Rust | |
|---|---|---|---|
| Infallible conversion | `int_of_char` (total function) | `From<T>` / `Into<T>` | |
| Fallible conversion | `int_of_string` raises exception | `TryFrom<T, Error=E>` | |
| Infallible marker | N/A (type system doesn't track) | `std::convert::Infallible` | |
| Generic over both | Manual dispatch | `TryFrom<T, Error=Infallible>` | |
| Zero-variant type | `type void = \ | ` (empty variant) | `enum Infallible {}` |
//! 310. Infallible conversions with Into
//!
//! `Infallible` marks conversions that cannot fail; `From<T>` implies `TryFrom<T, Error=Infallible>`.
use std::convert::{TryFrom, TryInto, Infallible};
/// Custom type that wraps a non-zero u32
#[derive(Debug, Clone, Copy, PartialEq)]
struct NonZeroU32(u32);
#[derive(Debug, PartialEq)]
struct ZeroError;
impl TryFrom<u32> for NonZeroU32 {
type Error = ZeroError;
fn try_from(n: u32) -> Result<Self, ZeroError> {
if n == 0 { Err(ZeroError) } else { Ok(NonZeroU32(n)) }
}
}
/// Infallible conversion: u8 always fits in u32
/// This is provided automatically via From<u8> for u32
#[allow(dead_code)]
fn demonstrate_from() {
let small: u8 = 255;
let big: u32 = u32::from(small); // infallible
println!("u8 {} -> u32 {}", small, big);
// TryFrom on u32->u8 is fallible
let large: u32 = 300;
let result = u8::try_from(large);
println!("u32 {} -> u8: {:?}", large, result);
}
/// Newtype with infallible From conversion
#[derive(Debug, PartialEq)]
struct Meters(f64);
impl From<f64> for Meters {
fn from(v: f64) -> Self { Meters(v) }
}
// From<f64> automatically gives us TryFrom<f64, Error=Infallible>
// We can verify:
fn use_try_from(val: f64) -> Result<Meters, Infallible> {
Meters::try_from(val)
}
fn main() {
// Standard library From impls
let n: u32 = u32::from(42u8); // infallible
println!("u8 -> u32: {}", n);
let s: String = String::from("hello"); // infallible
println!("&str -> String: {}", s);
// TryFrom fallible
let ok = NonZeroU32::try_from(5u32);
let err = NonZeroU32::try_from(0u32);
println!("TryFrom 5: {:?}", ok);
println!("TryFrom 0: {:?}", err);
// Into (reverse of From)
let m: Meters = 100.0f64.into(); // via From<f64>
println!("Meters: {:?}", m);
// use_try_from via TryFrom auto-impl
let result = use_try_from(42.0);
println!("Infallible TryFrom: {:?}", result);
demonstrate_from();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nonzero_ok() {
assert_eq!(NonZeroU32::try_from(5), Ok(NonZeroU32(5)));
}
#[test]
fn test_nonzero_err() {
assert_eq!(NonZeroU32::try_from(0), Err(ZeroError));
}
#[test]
fn test_from_infallible() {
let n: u32 = u32::from(100u8);
assert_eq!(n, 100);
}
#[test]
fn test_meters_from() {
let m: Meters = Meters::from(5.0);
assert_eq!(m, Meters(5.0));
}
}
(* 310. Infallible conversions with Into - OCaml *)
(* OCaml: conversion functions are plain functions *)
let () =
(* Infallible: i32 always fits in i64 *)
let x : int = 42 in
let y : int64 = Int64.of_int x in
Printf.printf "int -> int64: %Ld\n" y;
(* Infallible: any char is a valid string *)
let c = 'A' in
let s = String.make 1 c in
Printf.printf "char -> string: %s\n" s;
(* Fallible: string might not parse to int *)
let parse_int s = match int_of_string_opt s with
| Some n -> Ok n | None -> Error ("not a number: " ^ s)
in
Printf.printf "Parse '42': %s\n"
(match parse_int "42" with Ok n -> string_of_int n | Error e -> e);
Printf.printf "Parse 'abc': %s\n"
(match parse_int "abc" with Ok n -> string_of_int n | Error e -> e)