pub trait Modifier {
// Required method
fn apply(self, content: AnyPiece) -> AnyPiece;
}Expand description
A one-shot, by-value view transform (the SwiftUI ViewModifier analog): wrap a piece into a
new one. Pure composition, with no per-backend work. A plain FnOnce(AnyPiece) -> AnyPiece
closure is a Modifier too (the blanket impl below), so the common case needs no new type.
Apply one with Decorate::modifier.
AnyPiece here is a trade, not a requirement. It is not object safety: a Modifier is
never stored as dyn Modifier, and Decorate::modifier takes impl Modifier and applies
it on the spot. It is the closure impl below. A closure has one fixed parameter type, and
Rust has no for<P> FnOnce(P) -> _ bound (higher-ranked bounds range over lifetimes, not
types), so making apply generic over the content (fn apply<P: Piece>(self, c: P) -> Self::Out<P>, which a named modifier can satisfy with a GAT) would leave no closure able to
implement the trait at all. Pinning the input to the one piece type that accepts anything is
what keeps |p| … a modifier, and the erasure is what that costs, which is why this is the
single Decorate method that erases.
If a modifier ever needs to preserve its content’s type, parameterize the trait rather than
the method: trait Modifier<P: Piece> { type Out: Piece; fn apply(self, c: P) -> Self::Out; }.
Closures survive that (impl<P: Piece, O: Piece, F: FnOnce(P) -> O> Modifier<P> for F, and
their parameter still infers unannotated), while a named modifier such as day-piece-rating’s
Card becomes impl<P: Piece> Modifier<P> for Card with type Out = Decorated<P>. The bill
is a Decorate::modifier whose return type varies per modifier, a generic impl for every
named one, and a breaking change: not worth paying while the tree has two implementors.
Required Methods§
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".