almost-enough
Batteries-included ergonomic extensions for the enough cooperative cancellation crate.
While enough provides only the minimal Stop trait, this crate provides all concrete implementations, combinators, and helpers. It re-exports everything from enough for convenience.
Quick Start
use ;
let stop = new;
let stop2 = stop.clone; // Clone to share
// Pass to operations
assert!;
// Any clone can cancel
stop.cancel;
assert!;
Type Overview
| Type | Feature | Use Case |
|---|---|---|
Unstoppable |
core | Zero-cost "never stop" |
StopSource / StopRef |
core | Stack-based, borrowed, zero-alloc |
FnStop |
core | Wrap any closure |
OrStop |
core | Combine multiple stops |
Stopper |
alloc | Default choice - Arc-based, clone to share |
SyncStopper |
alloc | Like Stopper with Acquire/Release ordering |
ChildStopper |
alloc | Hierarchical parent-child cancellation |
StopToken |
alloc | Type-erased dynamic dispatch - Arc-based, Clone |
BoxedStop |
alloc | Type-erased dynamic dispatch (prefer StopToken) |
WithTimeout |
std | Add deadline to any Stop |
Features
std(default) - Full functionality including timeoutsalloc- Arc-based types,into_boxed(),child(), guards- None - Core trait and stack-based types only (
no_stdcompatible)
Extension Traits
The StopExt trait adds combinator methods to any Stop:
use ;
let timeout = new;
let cancel = new;
// Combine: stop if either stops
let combined = timeout.as_ref.or;
assert!;
cancel.cancel;
assert!;
Hierarchical Cancellation
Create child stops that inherit cancellation from their parent:
use ;
let parent = new;
let child = parent.child;
// Child cancellation doesn't affect parent
child.cancel;
assert!;
// But parent cancellation propagates to children
let child2 = parent.child;
parent.cancel;
assert!;
Stop Guards (RAII)
Automatically cancel on scope exit unless explicitly disarmed:
use ;
Type Erasure
Prevent monomorphization explosion at API boundaries with StopToken.
Stopper and SyncStopper convert to StopToken at zero cost via
Into — the existing Arc is reused, no double-wrapping:
use ;
Optimizing Hot Loops with dyn Stop
Use may_stop() to skip overhead for no-op stops behind &dyn Stop:
use ;
// Unstoppable: may_stop() = false, so stop is None — zero overhead
assert!;
StopToken and BoxedStop automatically optimize away no-op stops — when
wrapping Unstoppable, check() short-circuits without any vtable dispatch:
use ;
hot_loop.unwrap; // zero overhead
hot_loop.unwrap; // one dispatch per check
See Also
enough- Minimal core trait (for library authors)enough-tokio- Tokio CancellationToken bridgeenough-ffi- FFI helpers for C#, Python, Node.js
License
MIT OR Apache-2.0