Skip to content

← Trait Objects and Dispatch step 7 of 10

Medium Primitives

Dispatch through Rc: Rc<dyn Trait> and shared behaviour

Build a shared registry of handlers and implement these two functions:

pub fn broadcast(handlers: Vec<String>, events: Vec<i64>) -> Vec<i64>
pub fn share_counts(handlers: Vec<String>) -> Vec<usize>

The Handler trait and its five implementations are supplied. Store the registry as Vec<Rc<dyn Handler>>, with one entry per input name in the same order. Allocate one handler per distinct name, and make repeated names share it by cloning the Rc handle.

Broadcast every event

For each event, call every registry entry and sum its contribution. Return one total per event, preserving event order.

Name Contribution for event e
double e * 2
negate -e
addten e + 10
square e * e
any other string 0, using Ignore

Names match exactly, including case and whitespace. Distinct unknown strings still get distinct shared instances, even though both use Ignore. For example, "unknown" and "UNKNOWN" are separate names.

A repeated name contributes once per registry entry. With ["double", "double", "negate"] and event 3, the total is 6 + 6 - 3 = 9. An empty registry produces zero for every event; an empty event list produces an empty result.

Inputs used by this exercise keep the handler arithmetic and sums within i64. You do not need to define overflow behavior for other inputs.

Count the shared handles

share_counts builds the same registry, then returns Rc::strong_count for the first occurrence of each distinct name, in first-appearance order. Keep the full registry alive while measuring these counts.

["double", "double", "negate"] gives [2, 1]. ["other", "unknown", "other"] also gives [2, 1]: sharing follows the name, not the handler's concrete type.

A lookup map can reuse the handler for a repeated name. If the map stores Rc handles, release the map before counting. Otherwise it retains one additional strong reference for each distinct name.

Returning the registry from a helper is one way to do this: the helper's local map is dropped, while the returned vector keeps its handles. Do not create extra Rc clones just to inspect their counts. Borrow the existing entries instead. Empty input produces no counts.

Ownership and dispatch solve different problems

Rc keeps a value alive while multiple strong handles refer to it. Cloning an Rc increments its reference count; it does not clone the handler stored in the allocation. Rc is intended for sharing within one thread, not for sending handlers between threads.

The dyn Handler trait object lets the registry contain different handler types behind one interface. A call such as h.handle(event) dispatches to the implementation for that entry's concrete type.

Shared ownership does not require dynamic dispatch, and dynamic dispatch does not require shared ownership. They are combined here because this registry needs both. Box<dyn Handler> alone would give each box ownership of its own handler rather than provide shared Rc handles.

Avoid inferring a fixed number of machine instructions or a performance ranking from this type. Reference counting and indirect calls have costs, but their effect depends on the workload and compiler optimizations.

Repair the starter's type

The starter writes Rc<Handler>, which triggers E0782 in modern editions. Use Rc<dyn Handler> to name the trait-object type. The dyn keyword is required for this syntax from edition 2021 onward.

Rc::clone(&handle) makes the sharing operation explicit. Calling handle.clone() has the same effect when handle is an Rc. The optional clone_on_ref_ptr lint prefers the qualified form, but it is not enabled by default; the supplied solution uses method syntax on a map entry.

Complete both public functions as well as the registry helper. The submission must compile, match the totals and counts, and pass Clippy with warnings denied.