Minimal cooperative cancellation trait for Rust.
A no_std, zero-dependency trait for cooperative cancellation.
pub trait Stop: Send + Sync {
/// Check if the operation should stop.
/// Returns Ok(()) to continue, Err(StopReason) to stop.
fn check(&self) -> Result<(), StopReason>;
/// Returns true if the operation should stop (provided).
fn should_stop(&self) -> bool { self.check().is_err() }
/// Returns true if this stop can ever fire (provided).
/// Unstoppable returns false. Used by StopToken/BoxedStop to
/// optimize away no-op stops at construction time.
fn may_stop(&self) -> bool { true }
}One required method. Option<T: Stop> implements Stop: None is
a no-op, Some delegates — enabling the may_stop() optimization
pattern (see below).
Accept impl Stop + 'static in your public API. Use
StopToken
from almost-enough internally — it handles the Unstoppable optimization
automatically and is the fastest option for real stop types:
use enough::Stop;
use almost_enough::StopToken;
pub fn decode(data: &[u8], stop: impl Stop + 'static) -> Result<Vec<u8>, MyError> {
let stop = StopToken::new(stop); // Unstoppable → None (no alloc). Stopper → same Arc.
for (i, chunk) in data.chunks(1024).enumerate() {
if i % 16 == 0 {
stop.check()?; // Unstoppable: no-op. Stopper: one dispatch.
}
// process...
}
Ok(vec![])
}
// Callers:
// decode(&data, Unstoppable)?; // no cancellation — zero cost
// decode(&data, stopper)?; // with cancellationStopToken is Clone (Arc increment) for thread fan-out.
Stopper/SyncStopper convert to StopToken at zero cost via Into
(same Arc, no double-wrapping). Benchmarks show StopToken within 3%
of fully-inlined generic for Unstoppable, and 25% faster than generic
for Stopper.
Use &dyn Stop with may_stop().then_some():
fn inner(data: &[u8], stop: &dyn Stop) -> Result<(), MyError> {
let stop = stop.may_stop().then_some(stop); // Option<&dyn Stop>
for (i, chunk) in data.chunks(1024).enumerate() {
if i % 16 == 0 {
stop.check()?; // None → Ok(()), Some → one dispatch
}
}
Ok(())
}Use impl Stop (without 'static) to accept borrowed types like
StopRef<'a>:
fn process(data: &[u8], stop: impl Stop) -> Result<(), StopReason> {
for (i, byte) in data.iter().enumerate() {
if i % 64 == 0 { stop.check()?; }
}
Ok(())
}| Crate | Purpose |
|---|---|
enough |
Core trait: Stop, StopReason, Unstoppable |
almost-enough |
All implementations: Stopper, StopToken, StopSource, timeouts, combinators |
enough-ffi |
C FFI for cross-language use |
enough-tokio |
Bridge to tokio's CancellationToken |
Can't add a dependency? See ZERO-DEP.md.
- None (default) -
no_stdcore:Stoptrait,StopReason,Unstoppable alloc- AddsBox<T>andArc<T>blanket impls forStopstd- Impliesalloc(kept for downstream compatibility)
MIT OR Apache-2.0