4 releases
Uses new Rust 2024
| 0.1.3 | Nov 19, 2025 |
|---|---|
| 0.1.2 | Nov 14, 2025 |
| 0.1.1 | Oct 22, 2025 |
| 0.1.0 | Oct 22, 2025 |
#1504 in Network programming
255KB
3K
SLoC
anng
Safe, async Rust bindings for NNG (nanomsg next-generation).
What is NNG?
From the NNG GitHub repository:
NNG, like its predecessors nanomsg (and to some extent ZeroMQ), is a lightweight, broker-less library, offering a simple API to solve common recurring messaging problems, such as publish/subscribe, RPC-style request/reply, or service discovery. The API frees the programmer from worrying about details like connection management, retries, and other common considerations, so that they can focus on the application instead of the plumbing.
Why anng?
- Type-safe protocols: Compile-time prevention of protocol violations using Rust's type system.
- Async-first: Native async/await integration with Tokio and cancellation safety.
- Zero-cost abstractions: Memory safety and protocol correctness with no runtime overhead over NNG itself.
All NNG protocols are supported: REQ/REP, PUB/SUB, PUSH/PULL, SURVEYOR/RESPONDENT, BUS, and PAIR.
Quick Start
Add to your Cargo.toml:
[dependencies]
anng = "0.1"
tokio = { version = "1", features = ["full"] }
Request/Reply Example
use anng::{protocols::reqrep0, Message};
use std::io::Write;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Server
tokio::spawn(async {
let socket = reqrep0::Rep0::listen(c"tcp://127.0.0.1:8080").await?;
let mut ctx = socket.context();
let (request, responder) = ctx.receive().await?;
println!("Request: {:?}", request.as_slice());
let mut reply = Message::with_capacity(100);
write!(&mut reply, "Hello back!")?;
responder.reply(reply).await?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
});
// Client
let socket = reqrep0::Req0::dial(c"tcp://127.0.0.1:8080").await?;
let mut ctx = socket.context();
let mut request = Message::with_capacity(100);
write!(&mut request, "Hello server!")?;
let reply = ctx.request(request).await?.await?;
println!("Reply: {:?}", reply.as_slice());
Ok(())
}
Type Safety
The library uses phantom types to enforce protocol correctness at compile time:
// ✅ This compiles - correct protocol usage
let req_socket = reqrep0::Req0::dial(url).await?;
let reply = req_socket.context().request(msg).await?;
// ❌ This won't compile - protocol violation
let req_socket = reqrep0::Req0::dial(url).await?;
req_socket.publish(msg).await?; // Error: method `publish` not found
Socket types like Socket<reqrep0::Req0> only expose methods valid for
that protocol, making it impossible to accidentally call publish() on
a request socket or reply() before receiving a request.
Concurrency and Contexts
For concurrent operations, create contexts from sockets:
use std::sync::Arc;
let socket = Arc::new(reqrep0::Rep0::listen(url).await?);
// Spawn multiple workers for concurrent request handling
for worker_id in 0..4 {
let socket = Arc::clone(&socket);
tokio::spawn(async move {
let mut ctx = socket.context();
loop {
let (request, responder) = ctx.receive().await?;
// Handle request concurrently...
responder.reply(process_request(request)).await?;
}
});
}
Each context maintains independent protocol state, enabling safe concurrent operations on the same socket.
Cancellation Safety
All async operations are cancellation safe - futures can be dropped at any time without corrupting the NNG state. Cancelling a send may drop the message if dropped before NNG effects the send. Cancelling a receive will not drop an already-received message (in the case of a race). The library automatically handles NNG cleanup and message recovery when operations are cancelled.
Learning More
- Protocol concepts: See the NNG protocol documentation to understand when to use each messaging pattern
- API reference: Full documentation available on docs.rs
- NNG manual: https://nng.nanomsg.org/ref/preface.html
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Dependencies
~10–15MB
~212K SLoC