ch 0.1.0

A simple single threaded channel implementation
Documentation
  • Coverage
  • 75%
    3 out of 4 items documented0 out of 3 items with examples
  • Size
  • Source code size: 29.12 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 311.78 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 2s Average build duration of successful builds.
  • all releases: 2s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • dparnell/ch
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • dparnell

CH: Self-Contained Async Bi-Directional Channel

A minimal, single-threaded Rust library for bi-directional communication using async handlers, with zero external dependencies. It is purposely not possible to share channels across threads.

Features

  • Sync API, Async Logic: Completely hides async/await implementation details from the caller.
  • Stateful Handlers: Supports FnMut closures, allowing handlers to maintain and mutate their own state across requests.
  • Zero Dependencies: Built entirely on the Rust standard library (std).
  • Self-Sufficient: Each channel instance is independent and contains its own internal executor logic—no global state or background threads required.
  • Bi-Directional: Send a request and block until a response is received from an asynchronous handler.
  • Lightweight: Ideal for embedded systems or local task coordination where a full-blown async runtime is overkill.

Usage

Basic Example

use ch::Channel;

fn main() {
    // Create a channel with an async handler
    let channel = Channel::new(|val: i32| async move {
        val * 2
    });

    // Send items synchronously; the async logic is driven to completion internally
    let result = channel.send(42);
    assert_eq!(result, 84);
}

Stateful Handlers (FnMut)

You can capture and mutate state directly within the handler closure.

use ch::Channel;

let mut count = 0;
let channel = Channel::new(async move |val: i32| {
    count += val;
    let current = count;
    async move { current }
});

assert_eq!(channel.send(5), 5);
assert_eq!(channel.send(10), 15);

Nested Communication

Channels can be cloned and shared, even across other channel handlers.

use ch::Channel;

let doubler = Channel::new(|val: i32| async move { val * 2 });
let adder = Channel::new({
    let doubler = doubler.clone();
    move |val: i32| {
        let doubler = doubler.clone();
        async move {
            let doubled = doubler.send(val);
            doubled + 1
        }
    }
});

assert_eq!(adder.send(10), 21);

How it Works

ch implements a minimal "micro-executor" inside each send call. When you call send(), the library:

  1. Invokes your async handler to get a Future.
  2. Creates a local Waker tied to the current stack frame.
  3. Polls the future to completion in a loop, ensuring the library remains single-threaded and avoids global task queues.

Installation

Add this to your Cargo.toml:

[dependencies]
ch = "0.1.0"

Running Tests

cargo test

License

This project is licensed under the Creative Commons Attribution 4.0 International License. See the LICENSE file for details.