Thanks to visit codestin.com
Credit goes to lib.rs

#cooperative-cancellation #cancellation #ffi #async #no-std

no-std almost-enough

Batteries-included ergonomic extensions for the enough cooperative cancellation crate

3 unstable releases

new 0.3.1 Jan 18, 2026
0.3.0 Jan 14, 2026
0.2.0 Jan 11, 2026

#2989 in Rust patterns

Codestin Search App Codestin Search App Codestin Search App

185 downloads per month
Used in 2 crates

MIT/Apache

94KB
1.5K SLoC

almost-enough

Batteries-included ergonomic extensions for the enough cooperative cancellation crate.

CI Crates.io Documentation codecov License MSRV

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 almost_enough::{Stopper, Stop};

let stop = Stopper::new();
let stop2 = stop.clone();  // Clone to share

// Pass to operations
assert!(!stop2.should_stop());

// Any clone can cancel
stop.cancel();
assert!(stop2.should_stop());

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
BoxedStop alloc Type-erased dynamic dispatch
WithTimeout std Add deadline to any Stop

Features

  • std (default) - Full functionality including timeouts
  • alloc - Arc-based types, into_boxed(), child(), guards
  • None - Core trait and stack-based types only (no_std compatible)

Extension Traits

The StopExt trait adds combinator methods to any Stop:

use almost_enough::{StopSource, Stop, StopExt};

let timeout = StopSource::new();
let cancel = StopSource::new();

// Combine: stop if either stops
let combined = timeout.as_ref().or(cancel.as_ref());
assert!(!combined.should_stop());

cancel.cancel();
assert!(combined.should_stop());

Hierarchical Cancellation

Create child stops that inherit cancellation from their parent:

use almost_enough::{Stopper, Stop, StopExt};

let parent = Stopper::new();
let child = parent.child();

// Child cancellation doesn't affect parent
child.cancel();
assert!(!parent.should_stop());

// But parent cancellation propagates to children
let child2 = parent.child();
parent.cancel();
assert!(child2.should_stop());

Stop Guards (RAII)

Automatically cancel on scope exit unless explicitly disarmed:

use almost_enough::{Stopper, StopDropRoll};

fn do_work(source: &Stopper) -> Result<(), &'static str> {
    let guard = source.stop_on_drop();

    // If we return early or panic, source is stopped
    risky_operation()?;

    // Success! Don't stop.
    guard.disarm();
    Ok(())
}

Type Erasure

Prevent monomorphization explosion at API boundaries:

use almost_enough::{Stopper, BoxedStop, Stop, StopExt};

fn outer(stop: impl Stop + 'static) {
    // Erase the concrete type
    inner(stop.into_boxed());
}

fn inner(stop: BoxedStop) {
    // Only one version of this function exists
    while !stop.should_stop() {
        break;
    }
}

See Also

  • enough - Minimal core trait (for library authors)
  • enough-tokio - Tokio CancellationToken bridge
  • enough-ffi - FFI helpers for C#, Python, Node.js

License

MIT OR Apache-2.0

Dependencies