Description
Summary
I’m working on an actor-style framework in Rust where each tokio::mpsc::Sender represents an addressable “mailbox.” In order to implement zero-cost proxying and O(1) routing (e.g. forwarding messages to the correct actor instance in a proxy or fan-out scenario), I need a way to distinguish one Sender from another without resorting to global registries or linear scans with is_same_channel. Currently the internal Arc that backs the sender is private, so there is no stable, hash-able identifier exposed in the public API.
Use Case
Actor proxies & fan-out: I generate code that wraps a single actor interface into multiple channels. When a proxy receives a reply it needs to forward back to the original sender in O(1) time.
Zero-cost abstractions: I want to avoid boxing or adding manual integer IDs into every ActorRef; ideally the channel handle itself would carry identity.
High-performance routing: In a large actor system or microservices mesh, linear scans over potentially thousands of mailboxes create unacceptable overhead.
Example
// Without a stable ID, proxies must scan a Vec and call is_same_channel():
for target in targets.iter() {
if target.is_same_channel(&reply_to) {
target.send(reply_msg).await?;
break;
}
}
// O(n) scan—too slow at scale.
Proposal
Add one of the following to tokio::mpsc::Sender:
fn id(&self) -> usize
Returns a stable, unique identifier (e.g. pointer or hash of the internal Arc) that remains constant for the lifetime of the channel.
fn as_ptr(&self) -> *const ()
Exposes the raw, heap-allocated pointer of the internal Arc. Users can then safely compare or hash that pointer for identity.
With either API, proxy code can build a HashMap<usize, Sender> at setup time and do O(1) lookups for forwarding.
Alternatives Considered
Manual wrapper with atomic counter:
struct IdentifiedSender<T> { id: usize, sender: Sender<T> }
Works but pushes ID-management boilerplate onto every user and duplicates functionality that already exists internally in Tokio.
Unsafe Arc pointer extraction:
Transmute the Sender wrapper to its hidden Arc<...> and call Arc::as_ptr(). Reliant on Tokio internals and brittle across versions.
Global registries:
Central HashMap<SenderKey, Sender> guarded by a lock—adds complexity, overhead, and global mutable state.
Impact & Backwards Compatibility
This would be a non-breaking API addition.
Existing code using is_same_channel continues working unchanged.
Lightweight to implement: under the hood, id() could call Arc::as_ptr(...) as usize or similar.
Enables higher-level libraries (actor frameworks, RPC proxies, debugging/tracing tools) to build more efficient routing on top of Tokio channels.
Thanks for considering this enhancement! I’m happy to provide a PR or further details if the maintainers are open to adding a stable‐identity API for Sender.