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"). enums 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 between multiple types.

choice_at

Syntactic sugar for destructuring a Choice between multiple types.

choice_unreachable

Syntactic sugar for an unreachable Choice, which is only needed because the Rust exhaustiveness checker is unable to infer that Never is uninhabited.

Enums

Choice

Represents a choice between two types, which you can compose to represent a choice between more types -- Choice<C, Choice<A, B>> for instance.

Never

Represents an uninhabited type. This is a placeholder until the built-in never type is stabilized.