# whichever
[](https://crates.io/crates/whichever)
[](https://docs.rs/whichever)
[](https://github.com/shergin/whichever/actions/workflows/ci.yml)
Implements a trait for an enum by forwarding to whichever variant it holds.
```rust
// In the crate that owns the trait.
#[whichever::dispatchable]
pub trait Shape {
fn area(&self) -> u32;
fn name(&self) -> &'static str {
"shape"
}
}
// In any crate that owns an enum over implementors.
#[whichever::dispatch(Shape)]
pub enum AnyShape {
Circle(Circle),
Square(Square),
}
```
`AnyShape` now implements `Shape` with one `match` per method, each arm a
fully qualified call into the variant's payload, and gets `From<Circle>` and
`From<Square>`:
```rust
impl Shape for AnyShape {
#[inline]
fn area(&self) -> u32 {
match self {
AnyShape::Circle(inner) => <Circle as Shape>::area(inner),
AnyShape::Square(inner) => <Square as Shape>::area(inner),
}
}
#[inline]
fn name(&self) -> &'static str {
match self {
AnyShape::Circle(inner) => <Circle as Shape>::name(inner),
AnyShape::Square(inner) => <Square as Shape>::name(inner),
}
}
}
```
That is the code a careful hand would write: no trait object, no closure,
no conversion, and a variant's override of a default method is never hidden
by the default.
## Why another one
An enum over the implementors of a trait is the cheapest way to hold "one
of these": no allocation, no vtable, and the compiler sees every arm. The
forwarding impl is the price, paid again for every method and every variant.
`enum_dispatch` made it automatic, but it keeps the trait's definition in a
process-global cache inside the proc-macro server. That is why it cannot see
a trait from another crate (the enum compiles with no impl and the first
call fails), panics on associated types and receiver-less methods, and
loses the spans of its errors. `whichever` keeps nothing between
expansions: `#[dispatchable]` exports a hidden macro carrying the trait's
tokens, re-exported under the trait's own name, and `#[dispatch]` calls it.
The trait and the enum may live in different crates, in any order, and the
shapes real traits have (associated types, `&Self` arguments, generic
traits, several traits on one enum) dispatch too.
| Trait and enum in different crates | yes | no, silently | yes | yes | yes | yes | yes | yes, signatures re-listed |
| Default methods forwarded | yes | yes | yes | yes | yes | yes | yes | if listed |
| `Self`, `&Self`, `&mut Self` arguments | yes, with a declared fallback | no | no | no | panics at runtime | no | no | no |
| `-> Self` | yes | yes | no | no | yes | no | no | with `#[into]` |
| `-> Option<Self>`, `-> Result<Self, E>` | yes | no | - | - | - | - | - | - |
| Associated types | yes | no | yes | no | yes | no | no | by hand |
| Differing associated types unified | yes, through `Into` | no | no | no | yes, wrapper enum | no | no | by hand |
| Associated consts | yes | no | no | no | no | no | no | by hand |
| Methods without a receiver | with a default body | no | no | no | no | no | no | - |
| `async fn` | yes | yes | yes | no | no | no | yes | yes |
| Generic trait at a concrete instantiation | yes | no | yes | no | no | yes | no | by hand |
| Const generics on the trait | yes | - | - | - | - | - | - | - |
| Generic methods | yes | yes | yes | yes | no | yes | yes | yes |
| Lifetime-generic trait and enum | yes | no | yes | no | no | yes | no | by hand |
| Several traits on one enum | yes | yes | yes | yes | no | no | yes | yes |
| Payload with a same-named inherent method | safe | safe | safe | safe | safe | safe | safe | calls it, unless `#[through]` |
| `Box<T>`, `Rc<T>`, `&T` payloads | yes, `#[dispatch(deref)]` | no | no | no | no | no | no | yes |
| `-> impl Trait` | no | no | no | no | no | no | no | no |
| `From<Payload>` impls | yes, opt-out; `TryFrom<Enum>` opt-in | yes, plus a forced `TryInto` | no | no | yes | no | no | no |
| Inherent methods forwarded to some variants, with a fallback | yes | no | no | no | no | no | no | by hand |
Probed on 2026-11-01 against the versions shown, with the trait in one
crate and the enum in another, on stable Rust. A dash means the case was
not probed for that crate. The `whichever` column is what `tests/` checks.
## Options
All options follow the trait path in `#[dispatch(..)]`.
- `Name = Type` fixes the associated type `Name` instead of taking it from
the first variant. A method returning `Self::Name` is converted with
`Into`, one returning `Result<_, Self::Name>` with `map_err(Into::into)`,
so variants with their own error types can share one enum:
```rust
#[whichever::dispatch(Storage, Error = StorageError)]
pub enum AnyStorage {
Memory(MemoryStorage), Disk(DiskStorage), }
```
- `mismatch(method = expr, ..)` gives the result of `method` when its
`Self` arguments hold a different variant than `self`. The trait author
can decide once instead, with `#[dispatch(mismatch = expr)]` on the
method:
```rust
#[whichever::dispatchable]
pub trait Shape {
#[dispatch(mismatch = false)]
fn congruent(&self, other: &Self) -> bool;
}
```
- `from = false` suppresses the `From<Payload>` impls.
- `try_from = true` adds `TryFrom<Enum> for Payload` for every payload type,
with the enum as the error, so a mismatch hands the value back.
One option goes on a variant. `#[dispatch(deref)]` calls the trait on what
the payload dereferences to, so `Box<T>`, `Rc<T>`, `Arc<T>`, `&T` and
`&mut T` payloads dispatch like a plain `T`:
```rust
#[whichever::dispatch(Shape)]
pub enum AnyShape<'a> {
#[dispatch(deref)]
Boxed(Box<Circle>),
#[dispatch(deref)]
Borrowed(&'a Square),
Plain(Square),
}
```
Several traits are dispatched by repeating the attribute; a supertrait is
one more attribute:
```rust
#[whichever::dispatch(Shape)]
#[whichever::dispatch(Drawable)]
pub enum AnyShape { /* .. */ }
```
A generic trait is dispatched at a concrete instantiation:
`#[whichever::dispatch(Encoder<Json>)]`. Type, lifetime and const parameters
are substituted in every signature, and `F::Value` becomes
`<Json as Format>::Value` when `F` has one bound.
## Inherent methods
An enum often needs methods of its own that only some variants can answer.
`#[dispatch]` on an inherent `impl` block forwards each marked method to the
named variants' method of the same name; the body is what every other
variant returns. `*` names every variant, and the method then has no body:
```rust
#[whichever::dispatch]
impl AnyShape {
/// Only circles have a radius.
#[dispatch(Circle, with = Some)]
pub fn radius(&self) -> Option<f64> {
None
}
#[dispatch(*)]
pub fn describe(&self) -> String;
#[dispatch(*, call = describe)]
pub fn label(&self) -> String;
}
```
`with = path` wraps each forwarded result, `call = name` forwards to a
method of another name, `async` methods are awaited, and methods without
the attribute are left alone. The `impl` block has to be in the enum's
crate, which inherent impls are anyway; a bare `#[dispatch]` on an enum
registers it for this without dispatching a trait.
## What each trait item becomes
| `type X;` | `type X = <FirstVariant as Trait>::X;`, or the `X = Type` option. Every other variant is checked to agree; a disagreement is reported on that variant. |
| `const N: T;` | `const N: T = <FirstVariant as Trait>::N;` |
| method with a receiver | A `match` over the variants calling `<Payload as Trait>::method(payload, args..)`, with `#[inline]`. |
| default body | Ignored: forwarded like any other method. |
| `async fn`, `unsafe fn` | Arms are awaited; calls are wrapped in `unsafe { }`. |
| argument `Self`, `&Self`, `&mut Self` | Matched on the same variant as `self`; different variants give the `mismatch` expression. |
| return `Self` | Wrapped back into the same variant; `Option<Self>` and `Result<Self, E>` are mapped. |
| method without a receiver | Left to the trait's default body; an error when there is none. |
| `#[cfg]` on a method or variant | Carried onto the generated method or arm. |
## Limits
- Every variant holds exactly one unnamed field, `Name(Payload)`, and
every payload implements the trait.
- Return-position `impl Trait` cannot be dispatched by a `match`.
- A `deref` variant cannot take part in a method returning `Self`: there is
no way to put the result back behind the pointer.
- Generic associated types and `self: Type` receivers are not supported.
- The types in the trait's signatures are written into the enum's module
and must be in scope there, as for a hand-written impl; `crate::` paths
are rewritten to the trait's crate, `super::` and `self::` paths are not.
- Associated consts are taken from the first variant without a check.
Every unsupported shape is reported as one sentence on the offending trait
item or variant; see `tests/ui` for all of them.
"cannot find macro `Shape` in this scope" at the enum means the trait is not
marked `#[dispatchable]`, or the trait, which carries the macro, is not
imported where the enum is.
## Compatibility
`no_std`, with no dependencies beyond the proc-macro crate. The minimum
supported Rust version is 1.88. Both the crate that owns the trait and the
crate that owns the enum depend on `whichever`; renaming the dependency is
fine.
## License
Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or
[MIT license](LICENSE-MIT) at your option.