pub enum Imperfect<T, E, L>where
L: Loss,{
Success(T),
Partial(T, L),
Failure(E, L),
}Expand description
Result extended with partial success.
Three states:
Success(T)— perfect result, zero loss.Partial(T, L)— value present, some information lost getting here.Failure(E, L)— failure, no value, but the cost of getting here is measured.
The design descends from PbtA (Powered by the Apocalypse) tabletop games, which use three outcome tiers: 10+ is full success, 7-9 is success with complications, 6- is failure. The middle tier — success with cost — is the design innovation that PbtA contributed to game design. This crate encodes that structure in types.
Follows Result conventions: is_ok() means “has a value” (Success or Partial).
The .ok() and .err() extractor methods follow Result naming conventions.
Variants§
Success(T)
Perfect result, zero loss.
Partial(T, L)
Value present, some information lost getting here.
Failure(E, L)
Failure, no value, but the cost of getting here is measured.
Implementations§
Source§impl<T, E, L> Imperfect<T, E, L>where
L: Loss,
impl<T, E, L> Imperfect<T, E, L>where
L: Loss,
Sourcepub fn partial(value: T, loss: L) -> Imperfect<T, E, L>
pub fn partial(value: T, loss: L) -> Imperfect<T, E, L>
Construct a partial result with measured loss. Alias for Partial(value, loss).
Sourcepub fn failure_with_loss(error: E, loss: L) -> Imperfect<T, E, L>
pub fn failure_with_loss(error: E, loss: L) -> Imperfect<T, E, L>
Construct a failure carrying accumulated loss from prior steps.
Sourcepub fn is_partial(&self) -> bool
pub fn is_partial(&self) -> bool
Returns true if this is a Partial result.
Sourcepub fn ok(self) -> Option<T>
pub fn ok(self) -> Option<T>
Extract the value, discarding loss information. Returns None on Failure.
Sourcepub fn err_with_loss(self) -> Option<(E, L)>
pub fn err_with_loss(self) -> Option<(E, L)>
Extract the error and accumulated loss. Returns None on Success or Partial.
Unlike .err() which drops the loss, this returns both the error and the
loss that accumulated before the failure. This is information you can’t
recover any other way — L::total() can always be reconstructed from the
type, but the pre-failure loss cannot.
Sourcepub fn loss(&self) -> L
pub fn loss(&self) -> L
The loss incurred. Zero for Success, carried for Partial and Failure.
Failure carries the accumulated loss from before the failure — the cost
of getting here. If you need L::total(), check is_err().
Sourcepub fn as_ref(&self) -> Imperfect<&T, &E, L>
pub fn as_ref(&self) -> Imperfect<&T, &E, L>
Borrow the inner value and error without consuming self.
Sourcepub fn map<U>(self, f: impl FnOnce(T) -> U) -> Imperfect<U, E, L>
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Imperfect<U, E, L>
Transform the value, preserving loss and failure.
Sourcepub fn map_err<F>(self, f: impl FnOnce(E) -> F) -> Imperfect<T, F, L>
pub fn map_err<F>(self, f: impl FnOnce(E) -> F) -> Imperfect<T, F, L>
Transform the error, preserving value and loss.
Sourcepub fn eh<U>(
self,
f: impl FnOnce(T) -> Imperfect<U, E, L>,
) -> Imperfect<U, E, L>
pub fn eh<U>( self, f: impl FnOnce(T) -> Imperfect<U, E, L>, ) -> Imperfect<U, E, L>
Terni-functor bind. Chain an operation, accumulating loss.
- Success: apply f, return its result
- Partial: apply f, combine losses
- Failure: short-circuit, f never called
Sourcepub fn imp<U>(
self,
f: impl FnOnce(T) -> Imperfect<U, E, L>,
) -> Imperfect<U, E, L>
pub fn imp<U>( self, f: impl FnOnce(T) -> Imperfect<U, E, L>, ) -> Imperfect<U, E, L>
Alias for eh. The name. For the mischievous ones.
Sourcepub fn tri<U>(
self,
f: impl FnOnce(T) -> Imperfect<U, E, L>,
) -> Imperfect<U, E, L>
pub fn tri<U>( self, f: impl FnOnce(T) -> Imperfect<U, E, L>, ) -> Imperfect<U, E, L>
Alias for eh. Mathematical form — the terni-functor bind.
Sourcepub fn recover(
self,
f: impl FnOnce(E) -> Imperfect<T, E, L>,
) -> Imperfect<T, E, L>
pub fn recover( self, f: impl FnOnce(E) -> Imperfect<T, E, L>, ) -> Imperfect<T, E, L>
Attempt recovery from Failure. The loss carries forward.
Success and Partial pass through unchanged. Failure → recovery function → result carries the failure’s accumulated loss.
Recovery from Failure never produces Success — because the failure happened. The loss is real. The best you can do is recover a value and carry the cost.
Sourcepub fn unwrap_or_else(self, f: impl FnOnce(E) -> T) -> Imperfect<T, E, L>
pub fn unwrap_or_else(self, f: impl FnOnce(E) -> T) -> Imperfect<T, E, L>
Recover a default value from Failure. Always produces Partial. You can’t un-fail. But you can get something back. The loss survives.
Sourcepub fn unwrap_or(self, default: T) -> Imperfect<T, E, L>
pub fn unwrap_or(self, default: T) -> Imperfect<T, E, L>
Recover with a static default. Always produces Partial on Failure.
Sourcepub fn compose<T2, E2>(self, next: Imperfect<T2, E2, L>) -> Imperfect<T2, E2, L>where
E: Into<E2>,
pub fn compose<T2, E2>(self, next: Imperfect<T2, E2, L>) -> Imperfect<T2, E2, L>where
E: Into<E2>,
Propagate accumulated loss from self through next.
Deprecated in favor of eh / imp / tri.
Kept for backward compatibility.
- Success + next → next (no loss to propagate)
- Partial(_, loss) + Success(v) → Partial(v, loss)
- Partial(_, loss1) + Partial(v, loss2) → Partial(v, loss1.combine(loss2))
- Partial(_, loss1) + Failure(e, loss2) → Failure(e, loss1.combine(loss2))
- Failure(e, loss) + anything → Failure(e, loss) (short-circuits,
nextis discarded)
Trait Implementations§
Source§impl<T, L> From<Option<T>> for Imperfect<T, (), L>where
L: Loss,
None maps to Failure(()) because absence is total loss — there is no
value and no meaningful error to report. Some(v) maps to Success(v).
impl<T, L> From<Option<T>> for Imperfect<T, (), L>where
L: Loss,
None maps to Failure(()) because absence is total loss — there is no
value and no meaningful error to report. Some(v) maps to Success(v).