We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Trait Objects and Dispatch step 2 of 10
Keep concrete methods while enabling trait objects
Make the supplied Render trait usable through Box<dyn Render>. Add
where Self: Sized to the tagged and duplicated methods, keeping both
methods available on Dot and Word. Do not delete methods, change their
behavior or require Sized on the trait itself.
The starter fails with E0038 because tagged has a type parameter and duplicated returns Self. label can remain callable through the trait object.
pub fn dyn_labels(specs: Vec<String>) -> Vec<String>
pub fn concrete_report(kind: String, payload: String) -> Vec<String>
Preserve the supplied parsing and output
dyn_labels processes each spec independently, preserving order and duplicates:
- Split at the first colon. If the part before it is exactly
"dot", parse the remainder as usize and return that many dots. A failed parse means zero dots, or the empty string. - For any other prefix before a colon, uppercase the entire remainder.
- With no colon, uppercase the entire spec.
There is no trimming. For example, "dot:+03" gives "...", "dot: 3"
gives "", "word:a:b" gives "A:B", and "DOT:hi" gives "HI".
Dot parsing accepts decimal ASCII digits, an optional leading plus and
leading zeros; negative, empty, malformed or out-of-usize-range counts fail.
Empty specs input produces an empty vector.
concrete_report returns two strings:
| Kind | First string | Second string |
|---|---|---|
Exactly "dot" |
One dot per UTF-8 byte of payload, then #, then the decimal byte count. |
Twice that many dots. |
| Any other string | Uppercased payload followed by #cap. |
Payload concatenated with itself, then uppercased. |
For kind dot and payload "é", the result is ["..#2", "...."]. For kind
word and payload "hi", it is ["HI#cap", "HIHI"]. The dot branch uses
payload's byte length, not a number parsed from its contents.
Uppercasing is Unicode-aware and not locale-specific; it can expand text, as with ß becoming SS. Other characters, including whitespace, are preserved unless uppercasing changes them. Unicode tables can vary with Rust versions. Dot output allocates one byte per dot; the supplied implementation assumes the requested output fits available memory and the doubled byte count fits usize.
What the method bound changes
Dyn compatibility, formerly called object safety, determines whether a trait can be used as the base of a trait object. A trait object provides an erased interface to a concrete implementation. Calls through that interface use dispatch information; its exact vtable layout is not a stable API contract.
A method with where Self: Sized is excluded from that interface. The erased
dyn Render type is unsized, so this exercise's objects can call label but
cannot call tagged or duplicated through Render. Dot and Word are sized and
retain all three methods. Put the restriction on the two methods, not on
Render itself.
Here is a complete example of the same pattern:
trait Describe {
fn label(&self) -> String;
fn repeat(&self, count: usize) -> String where Self: Sized {
self.label().repeat(count)
}
}
struct Mark;
impl Describe for Mark {
fn label(&self) -> String { "x".to_string() }
}
fn main() {
assert_eq!(Mark.repeat(3), "xxx");
let object: Box<dyn Describe> = Box::new(Mark);
assert_eq!(object.label(), "x");
}
Adding object.repeat(3) would fail because that method requires a sized
implementor. The Box itself being sized does not make its dyn Describe
referent sized.
Check the specific compatibility restriction
Type- or const-generic methods cannot be dispatched through an ordinary trait object; lifetime parameters are allowed. Returning Self also requires a concrete result type that the erased interface does not provide. Other restrictions concern supertraits, associated items, receiver types and opaque return types such as those from async methods.
Use the dyn compatibility reference for the detailed rules. A method-level Sized restriction addresses these two methods; it is not a universal repair for every incompatible trait.
Associated types are a different issue. This complete example specifies Iterator's Item type and uses the resulting trait object:
fn main() {
let mut values: Box<dyn Iterator<Item = u32>> = Box::new(1..3);
assert_eq!(values.next(), Some(1));
assert_eq!(values.next(), Some(2));
assert_eq!(values.next(), None);
}
Writing Box<dyn Iterator> here instead produces E0191 for the missing Item
binding. That is distinct from the starter's E0038.
A different API could accept &dyn Display instead of a generic tagged
argument, or return a boxed trait object instead of Self. Those choices
change how callers use the methods. This exercise preserves the existing
concrete API and opts its two methods out of dynamic dispatch.
Passing requires both report functions to keep working, compilation and Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.