mailbox: Phase 4b — send_dyn bare-pid escape hatch (RFC 014 §4.6)

The explicit fallible fallback for when only an untyped Pid is in hand (off a
Down, out of a future members()), so the typed send_to / send-by-name stay the
default and callers don't grow workarounds around a missing primitive.

- send_dyn::<M>(Pid, msg): one line over the shared send_to_pid core. Same
  identity-bound, no-redirect liveness as send_to (Dead once the incarnation is
  gone); the difference is the explicit M, so NoChannel is a real runtime
  outcome here rather than the debug_assert it collapses to on the typed paths.
- Takes Pid<Erased> specifically: a typed Pid<A> must .erase() to reach it, so
  opting into the fallible downcast stays visible at the call site.

tests/registry.rs: send_dyn_delivers_and_reports_wrong_type (right type
delivers; live actor with no channel for the asked type -> NoChannel) and
send_dyn_to_dead_pid_is_dead. Full suite + order-checker green, warning-free.

This completes the RFC 014 send surface: Name<M> (re-resolving), Pid<A>
(typed direct), send_dyn (bare-pid). send_after (§6) can now target any of
them — its Dest question is answered.
This commit is contained in:
smarm-agent
2026-06-17 11:45:48 +00:00
parent 3cec3ba1a1
commit 4c56938f0b
3 changed files with 63 additions and 3 deletions
+18
View File
@@ -398,3 +398,21 @@ fn send_to_pid<M: Send + 'static>(
pub fn send_to<A: Addressable>(pid: Pid<A>, msg: A::Msg) -> Result<(), SendError<A::Msg>> {
with_runtime(|inner| send_to_pid::<A::Msg>(inner, pid.erase(), msg))
}
/// The explicit bare-pid escape hatch (RFC 014 §4.6): deliver `msg` of type `M`
/// to `pid` when all you hold is an untyped [`Pid`] — a pid off a [`Down`], or
/// out of a future `members()` — so the typed [`send_to`] is unavailable.
///
/// This is the one send whose message type can genuinely be wrong: the actor
/// may be live yet expose no channel for `M`, returning [`SendError::NoChannel`]
/// (on the typed paths that downcast collapses to a `debug_assert`). It is
/// named and documented as the fallible fallback so the typed `Pid<A>` /
/// `Name<M>` paths stay the obvious default and an agentic caller reaches for a
/// present primitive instead of inventing a workaround. Liveness is identical
/// to [`send_to`]: identity-bound, no redirect, [`SendError::Dead`] once the
/// addressed incarnation is gone. Panics if called outside `Runtime::run()`.
///
/// [`Down`]: crate::Down
pub fn send_dyn<M: Send + 'static>(pid: Pid, msg: M) -> Result<(), SendError<M>> {
with_runtime(|inner| send_to_pid::<M>(inner, pid, msg))
}