We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership II: Borrowing and the Borrow Checker step 1 of 24
Move or borrow: the two ways to pass a value
Implement two functions that read borrowed text:
pub fn char_count(text: &str) -> usize
pub fn shout(text: &str) -> String
char_count returns the number of Unicode scalar values (chars) in the
original text. shout returns a new String containing Rust's Unicode
uppercase conversion of the text followed by one additional !.
Preserve spaces and existing punctuation. Neither function changes the
caller's text.
| Input | Count | Shout |
|---|---|---|
"" |
0 | "!" |
"hi" |
2 | "HI!" |
"héllo" |
5 | "HÉLLO!" |
"Ok!" |
3 | "OK!!" |
"ß" |
1 | "SS!" |
The starter counts bytes instead of scalar values and leaves shout
unfinished. Keep both function signatures and fix those two bodies.
Borrowing keeps the caller's string available
Passing a String by value transfers ownership. Passing &text to these
functions borrows its contents instead, so the caller can use it again:
let text = String::from("héllo");
assert_eq!(char_count(&text), 5);
assert_eq!(shout(&text), "HÉLLO!");
assert_eq!(text, "héllo");
Not every by-value argument moves an owned allocation. Values such as
integers and shared references implement Copy; passing them by value
copies them. Copying a &str copies its pointer and length, not the text's
bytes.
Safe borrowing prevents a reference from being used after its referent is
gone. References must be valid and non-null; optional borrowed data can use
Option<&T>. A reference is not necessarily short-lived: string literals,
for example, have type &'static str.
Why the parameters use &str
These functions need text, not the ownership or capacity of a String.
&str accepts string literals, borrowed String contents and string slices:
let text = String::from("hello");
assert_eq!(char_count("hi"), 2);
assert_eq!(char_count(&text), 5);
assert_eq!(char_count(&text[1..4]), 3);
The borrow of String is coerced to a string slice at the call. Slicing
uses byte offsets, so both endpoints must be valid UTF-8 boundaries; the
ASCII slice above is valid. See Borrow str instead of String
for more on choosing borrowed parameter types.
Changing a parameter to String would make the harness's existing &text
call a type mismatch. If you also changed that call to pass text by
value, subsequent use of the moved string would fail. Keep the requested
borrowing interface.
Count scalar values, then build a separate output
str::len() reports bytes. "héllo" occupies six bytes but contains five
scalar values. Use the chars iterator for the requested count.
Scalar values are not always the characters a reader perceives. For example,
"e\u{301}" contains e followed by a combining acute accent: two scalar
values, even when displayed as one accented letter. Count both; do not
normalize or combine them.
Uppercasing can change length. The ß example becomes SS, while the
count still describes the original input. Use str::to_uppercase to build
the owned uppercase result, then append one !. ASCII-only conversion
would miss non-ASCII letters.
Submissions must compile, pass the cases and pass the Rust Clippy gate.
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.