We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Trait Objects and Dispatch step 7 of 10
Dispatch through Rc: Rc<dyn Trait> and shared behaviour
Box<dyn Trait> gives you one owner of an erased type. But an observer
registry — the shape behind every event bus, every plugin table, every “call
these five things when something happens” — usually wants the same handler
registered in several places. One owner is not enough. You want shared
ownership of an erased type, and that is Rc<dyn Trait>.
This is Track 11 and Track 12 meeting: reference counting plus dynamic dispatch, in the one composition that appears constantly in real code.
What Rc<dyn Handler> actually is
Rc<T> is a thin pointer to a heap box holding { strong, weak, T }.
Rc<dyn Handler> is a fat pointer to that box: a data pointer plus a
vtable pointer. So calling h.handle(e) costs two hops — one through the Rc
to the box, one through the vtable to the function. That is the
double-indirection point from the smart-pointer track, now in a dispatch
setting, and it is why an Rc<dyn _> in a hot inner loop is one of the
slowest shapes you can write.
Cloning it is cheap and does not clone the handler: it bumps a counter and copies two words. That is the whole point.
What to write
A Handler trait and five implementors are given. Build the registry —
Vec<Rc<dyn Handler>> — with one Rc instance per distinct name, and
make repeated names share that instance via Rc::clone.
pub fn broadcast(handlers: Vec<String>, events: Vec<i64>) -> Vec<i64>
For each event, in order, return the sum of what every registry entry
returns for it. A duplicated handler contributes twice — it is in the registry
twice, even though there is only one instance of it in memory. An empty
registry sums to 0.
The names are "double" (e * 2), "negate" (-e), "addten" (e + 10)
and "square" (e * e). Any other name maps to the Ignore handler,
which contributes 0.
pub fn share_counts(handlers: Vec<String>) -> Vec<usize>
Build the same registry, then return Rc::strong_count for the first
occurrence of each distinct name, in first-appearance order.
Read that carefully, because it pins down a real subtlety. If you build the
registry with a lookup table (HashMap<&str, Rc<dyn Handler>>) and the table
is still alive when you count, every count is one higher, because the table
holds a strong reference too. Drop the lookup table before counting. Then
strong_count is exactly the number of times that name appeared —
["double", "double", "negate"] gives [2, 1].
That is not a trick question. It is the thing that makes Rc counts
confusing in real programs: the count answers “how many strong handles exist
right now“, and caches, closures and temporaries all hold handles.
The error the starter hands you
E0782 — expected a type, found a trait. Writing Rc<Handler> instead of
Rc<dyn Handler> was a lint in edition 2015 and is a hard error from
edition 2021 onwards. The compiler is not being pedantic: Handler alone is
ambiguous between “the trait, as a bound” and “some value implementing it”,
and the two compile to completely different code.
Lints in the neighbourhood
-
redundant_allocation—Rc<Box<dyn Handler>>is two heap boxes and two indirections where one would do.Rc<dyn Handler>already handles the unsized type. -
borrowed_box—&Box<dyn Handler>should be&dyn Handler. -
clone_on_ref_ptr(restriction, off by default) — argues for writingRc::clone(&x)rather thanx.clone(), so that a reader can see at a glance that this is a refcount bump and not a deep copy. It is off by default and the community is split, but the argument is worth knowing.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.