Crate choice[−][src]
Rust has a built in tuple (A, B, C, ...)
to represent a "product" or "intersection" of types.
The language lacks a generic syntax for the converse: a choice among multiple types, also known
as a union type A + B + C ...
in other programming
languages (which
are also related to "sum types" and "coproducts"). enum
s serve a similar role but must be
named, which also makes trait implementation more cumbersome when you want a trait to be
applicable to N different variants.
Choice
provides a pattern and some syntactic sugar to bridge this gap.
The pattern may be a bit counterintuitive the first time you see it. The first step is to use
Choice::new
to build a base variant on top of Never
:
use choice::{Choice, Never}; let no_real_choice: Choice<u64, Never> = Choice::new(42);
The Never
type is uninhabitable and only serves to seed the pattern, so effectively we have a
"choice" between N=1 types in the example above, only u64
. Calling Choice::or
extends a
type to offer one more choice, inductively enabling a choice between N+1 types.
let two_types_option_1: Choice<&'static str, Choice<u64, Never>> = Choice::new(42).or();
You can build an instance of the same Choice
type that holds the other inner type by simply
calling Choice::new
:
let two_types_option2: Choice<&'static str, Choice<u64, Never>> = Choice::new("Forty two");
The above two examples share a type, so you can embed them in a collection:
let u64_or_string_vec: Vec<Choice<&'static str, Choice<u64, Never>>> = vec![ Choice::new(42).or(), Choice::new("Forty two"), Choice::new(24).or(), Choice::new("Twenty four"), ];
This pattern composes to allow additional choices:
let many: Vec<Choice<&'static str, Choice<i8, Choice<char, Never>>>> = vec![ Choice::new("-INFINITY"), Choice::new(-1 ).or(), Choice::new('0' ).or().or(), Choice::new(1 ).or(), Choice::new("INFINITY" ), ];
Macros
The choice!
macro provides syntactic sugar for a type representing the above pattern, which
is particularly useful when there are many choices:
let x1: choice![u64, &'static str, char, String, i8] = Choice::new('x').or().or(); let x2: Choice<u64, Choice<&'static str, Choice<char, Choice<String, Choice<i8, Never>>>>> = Choice::new('x').or().or(); assert_eq!(x1, x2);
The choice_at!
macro provides syntactic sugar for pattern matching on a choice. Rust is
unable to determine that the base case Never
in uninhabited, so there is also a
choice_unreachable!
macro to appease the exhaustiveness
checker.
let c: choice![u8, char] = Choice::new('2').or(); match c { choice_at!(0, v) => { panic!("Unexpected match: {}", v); } choice_at!(1, v) => { assert_eq!(v, '2'); } choice_unreachable!(2) => { unreachable!(); } }
Macros
choice | Syntactic sugar for a type representing a |
choice_at | Syntactic sugar for destructuring a |
choice_unreachable | Syntactic sugar for an unreachable |
Enums
Choice | Represents a choice between two types, which you can compose to represent a choice between more
types -- |
Never | Represents an uninhabited type. This is a placeholder until the built-in never type is stabilized. |