We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 11 of 24
AsRef, Borrow, and signatures your callers can actually use
Here is a function that works and is still wrong:
pub fn total_len(items: &Vec<String>) -> usize
Two things are wrong with it, and clippy tells you about one:
error: writing `&Vec` instead of `&[_]` involves a new object where a slice
will do [clippy::ptr_arg]
ptr_arg is default-on and it teaches the whole “take the borrowed form”
lesson by itself. &Vec<T> accepts only a Vec; &[T] accepts a Vec, an
array, a slice of either, and a sub-range of any of them. You lose nothing and
gain every caller. The same rule gives you &str over &String.
The second problem ptr_arg cannot help with: the element type is nailed down
to String. A caller holding a Vec<&str> is out of luck. That is what
AsRef is for.
AsRef<str>
pub fn total_len<S: AsRef<str>>(items: &[S]) -> usize
Read the bound as: “S is some type from which I can cheaply borrow a &str“.
String implements it. &str implements it. Box<str> implements it. Your
own newtype can implement it in three lines. One signature, all of them.
Two facts about AsRef that beginners get wrong:
-
It is not transitive.
A: AsRef<B>andB: AsRef<C>does not give youA: AsRef<C>. There is no blanket chain, and people expect one. - It is a cheap reference-to-reference conversion by contract. Not a parse, not an allocation. If your impl allocates, callers will be surprised.
Your task
pub fn total_len<S: AsRef<str>>(items: &[S]) -> usize
pub fn run(words: Vec<String>) -> Vec<usize>
total_len sums the byte lengths (str::len, not character count) of its
items. run calls it twice on the same data — once over the Vec<String> it
was given, and once over a Vec<&str> borrowed from it — and returns both
answers. They must be identical. That identity is the whole demonstration:
one generic function serving two different element types.
Note the byte-length detail in the tests. "héllo" is five characters and
six bytes; str::len reports bytes.
Both directions are policed
Learners who take the “use references” advice too enthusiastically meet the other side of the gate:
error: this expression creates a reference which is immediately dereferenced
by the compiler [clippy::needless_borrow]
and its relative clippy::needless_borrows_for_generic_args, which fires when
you write &x for a generic parameter that would have taken x happily. So
the rule is not “always add &“ — it is “borrow at the boundary, and let
coercion do the rest”.
AsRef vs Borrow — the distinction worth knowing
Borrow<T> has the same shape as AsRef<T> and one extra clause in its
contract:
Eq,OrdandHashon the borrowed form must be equivalent to those on the owned form.
That extra promise is what makes heterogeneous map lookup sound. This works:
let map: HashMap<String, i32> = ...;
map.get("key") // &str probe against String keys
because HashMap::get is bounded on Borrow, and String: Borrow<str>
guarantees that hashing the &str gives the same value as hashing the
String. If get were bounded on AsRef, a type whose AsRef<str> returned
something unrelated to its own hash would silently break the map.
And here is the part that matters: the compiler cannot verify that promise.
It is a semantic obligation on the implementor, exactly like Hash/Eq
agreement. Choose Borrow when your conversion preserves identity; choose
AsRef when it is merely a convenient view.
(One deny-level lint guards a specific instance of this:
clippy::impl_hash_borrow_with_str_and_bytes, which rejects a type that
implements both Borrow<str> and Borrow<[u8]> alongside Hash — since
str and [u8] hash differently, one of the two lookups would be broken.)
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.