We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 13 of 24
Choose the intended method through a wrapper
A wrapper and its dereference target can offer methods with the same name.
Two traits can also define the same method. Practice selecting the intended
implementation by completing counts in the supplied starter.
pub fn counts(slots: Vec<String>) -> Vec<usize>
Return four numbers in this order:
| Position | Meaning | Supplied implementation |
|---|---|---|
| 0 | Number of non-empty strings | Inventory::len |
| 1 | Total number of slots, including empty strings | Vec::len |
| 2 | Sum of all string lengths in UTF-8 bytes | Weight::total |
| 3 | Ten times the total slot count | Bulk::total |
Only edit the body of counts; keep the supplied types and implementations.
Strings may contain arbitrary Unicode or control characters. Only "" is
empty: spaces and newlines count as non-empty strings. Repeated strings each
contribute to the totals. Inputs fit in memory, and both the byte sum and ten
times the slot count fit in usize.
For ["axe", "", "rope"], return [2, 3, 7, 30]. For ["é", "", "ßß"],
return [2, 3, 6, 30]: UTF-8 uses two bytes for each of these accented
characters. No slots produce [0, 0, 0, 0].
Select the wrapper or the vector
Inventory implements Deref<Target = Vec<String>>, and it also defines
its own len(&self). In this setup, inv.len() resolves to the wrapper's
inherent method before lookup reaches the vector. That is the correct call
for the first output, but the wrong one for the second.
To request the vector's length, use Vec::len(&inv) and let the reference
coerce, or dereference once before lookup with (*inv).len().
This complete example demonstrates the same distinction with a small wrapper:
use std::ops::Deref;
struct Slots(Vec<String>);
impl Deref for Slots {
type Target = Vec<String>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Slots {
fn len(&self) -> usize {
self.0.iter().filter(|s| !s.is_empty()).count()
}
}
fn main() {
let slots = Slots(vec![String::from("x"), String::new()]);
assert_eq!(slots.len(), 1);
assert_eq!(Vec::len(&slots), 2);
assert_eq!((*slots).len(), 2);
}
These calls borrow the vector to read its length; the explicit * does not
move its contents out. Method lookup depends on receiver types and borrowing,
so avoid turning this example into a universal rule that any inherent method
always wins. The method-call reference
specifies the candidate order.
Select a trait explicitly
Both Weight and Bulk implement total(&self) for Inventory. With both
traits in scope, inv.total() is ambiguous and produces E0034. Rust cannot
infer which total you want from its return type; both return usize.
Use Weight::total(&inv) for the byte count and Bulk::total(&inv) for the
bulk count. You can spell the first as <Inventory as Weight>::total(&inv)
to identify both the implementing type and the trait. If the trait name itself
is ambiguous, qualify its module path too.
The starter therefore has two issues: its second len() call chooses the
wrong count, and its two total() calls do not compile. Resolve both while
preserving the required output order.
The API design tradeoff
This wrapper deliberately gives len a different meaning from its target.
A clearer domain API might use occupied_count() and slot_count() without
dereferencing to a vector. That makes the choice visible in the method names.
Deref is useful for types that should transparently expose a borrowed target,
including smart pointers and collections such as String and Vec. It is
not class inheritance, and it does not transfer every trait implementation
from the target to the wrapper. Implicit method access is a public API choice:
new methods can collide, and removing the dereference implementation can break
callers. Being a newtype alone does not settle those design questions.
Deref alone supplies shared access. This starter has no DerefMut, so it
does not let you call the vector's clear() or push() through the wrapper.
Adding DerefMut would permit mutable access too, potentially bypassing any
update rules that the wrapper was meant to enforce.
An explicit accessor or AsRef can expose a selected borrowed view.
Borrow additionally requires the owned and borrowed forms to agree on
equality, ordering and hashing where applicable. Neither trait enables the
target's methods through automatic receiver dereferencing as Deref does.
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.