We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 5 of 24
Borrowing through Option
Read two optional fields, return the name unchanged, and collect byte lengths:
pub fn summarize(
name: Option<String>,
tags: Option<Vec<String>>,
) -> (usize, Option<String>, Vec<usize>)
The function takes ownership of both arguments. While calculating lengths,
borrow their contents so that you can return the original name without
cloning it. Return the tuple in this order:
- The name's byte length, or
0forNone. - The original
name, preservingNoneversusSome(""). - Each tag's byte length in input order. Both
Noneand an empty tag vector give an empty length vector.
The tags are not returned; they are dropped when the function finishes. Keep duplicates and empty tags in the output rather than filtering them.
| Name | Tags | Result |
|---|---|---|
Some("ada") |
Some(["rust", "errors"]) |
(3, Some("ada"), [4, 6]) |
None |
Some(["a"]) |
(0, None, [1]) |
Some("") |
None |
(0, Some(""), []) |
Some("héllo") |
Some(["ü", ""]) |
(6, Some("héllo"), [2, 0]) |
The table omits the String and Vec constructors for readability.
String::len counts UTF-8 bytes. chars().count() counts Unicode scalar
values, which is a different measure and is not always the number of
characters a reader perceives.
Why the starter fails
name.as_ref() borrows the option's contents, producing Option<&String>.
The starter then calls .map(|s| *s). Its closure receives a shared reference
and tries to move the String out through it. Rust rejects that move with
E0507 because the borrowed value must remain available to its owner.
Compute the length through the reference instead. map consumes the option
it is called on, so use an option containing references when the original
owned option must remain available. Calling name.map(...) directly would
move name and prevent returning it later in this implementation.
Choose the kind of access you need
For name: Option<String>, these methods have different results:
| Expression | Result | Purpose |
|---|---|---|
name.as_ref() |
Option<&String> |
Read the contained string through a shared reference |
name.as_mut() |
Option<&mut String> |
Modify it through a mutable reference; requires mutable access to name |
name.as_deref() |
Option<&str> |
Borrow through String's Deref implementation |
name.as_ref().cloned() |
Option<String> |
Clone the contained string when present |
For a copyable value, Option<&T>::copied() produces Option<T>. The
Option documentation
describes the bounds and variants of these methods.
This complete example separates borrowing, mutation, cloning and copying:
fn main() {
let mut name = Some(String::from("ada"));
assert_eq!(name.as_deref(), Some("ada"));
let snapshot = name.as_ref().cloned();
if let Some(text) = name.as_mut() {
text.push('!');
}
assert_eq!(name.as_deref(), Some("ada!"));
assert_eq!(snapshot.as_deref(), Some("ada"));
let number = 7;
let borrowed_number = Some(&number);
assert_eq!(borrowed_number.copied(), Some(7));
}
The snapshot needs independent ownership because the original is modified afterwards. Reading a string's length does not need such a snapshot.
Apply borrowing to this task
Use as_deref() to view the name as Option<&str>. Then
map_or(0, str::len) supplies zero for None and computes the length for
Some. The default argument comes first.
For the tags, as_deref() produces Option<&[String]>.
unwrap_or_default() gives an empty slice when the option is absent. Iterate
over that slice and collect each length into the result vector. Obtaining
the borrowed slice does not clone the tags; collecting lengths creates the
separate output vector.
Keep #![deny(clippy::redundant_clone)] from the starter. It rejects the
redundant-clone patterns that Clippy detects, not every possible clone or
allocation. Passing value tests or a lint check alone does not prove that
an implementation preserved the original allocation. Follow the task's
borrowing requirement directly.
A useful helper signature
A helper that only needs optional text can accept Option<&str>:
fn name_len(name: Option<&str>) -> usize {
name.map_or(0, str::len)
}
fn main() {
let owned = Some(String::from("ada"));
assert_eq!(name_len(owned.as_deref()), 3);
assert_eq!(name_len(Some("grace")), 5);
assert_eq!(name_len(None), 0);
}
This lets a caller pass text without first building an Option<String>.
It is an API choice, not a rule that &Option<T> is always wrong. A helper
may need access to the container itself. Constructing an Option also does
not inherently require heap allocation; the contained value determines
what storage it needs.
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.