Getting the length
The method len() returns the length of a string:
let mut message = String::from("Hello world");
println!("{}", message.len());
message += "!";
println!("{}", message.len());
11
12
The result is a usize, an unsigned integer. It can't be negative, and that matters as soon as you subtract from it.
Length counts bytes
In Rust, len() returns the number of bytes in the string's UTF-8 encoding, not the number of characters. For plain ASCII text, which covers English letters, digits, spaces and common punctuation, the two are equal. For other text they differ:
| Text | len() | chars().count() |
|---|---|---|
"Hello world" | 11 | 11 |
"héllo" | 6 | 5 |
"日本語" | 9 | 3 |
Use len() when you want the size in bytes, or when you know the text is ASCII, as in this lesson. Use chars().count() when you want the number of characters; it has to walk through the whole string, so it is slower. An empty string has length 0, and is_empty() is the clearest way to test for it.
Characters from the end
Positions start at 0, so the last character is at position len() - 1. Rust strings can't be indexed by a single number, so you take a one-character slice from that position. A range with no end, such as length - 1.., runs to the end of the string:
let message = String::from("Hello world");
let length = message.len();
println!("{}", &message[length - 1..length]);
println!("{}", &message[length - 2..length - 1]);
d
l
In general, the n-th character from the end is &message[length - n..length - n + 1].
The "minus one" comes from counting. A string of length 11 has positions 0 to 10, so position 11 does not exist. The end of a range is excluded, which is why the one-character slice at the end can be written as length - 1..length or just length - 1..: both stop after the last byte. The same arithmetic works for any position counted from the end, and it never needs the string to be rewritten or reversed.
Slicing the last n characters
To take the last n characters, start at length - n and leave the end open:
let message = String::from("Hello world");
let length = message.len();
let last_chars = &message[length - 4..];
println!("{}", last_chars);
let last_chars = &message[length - 7..];
println!("{}", last_chars);
orld
o world
The second let last_chars is a new variable that hides the first one. This is called shadowing and is allowed in Rust. Each slice is a borrowed &str, so nothing is copied.
The lesson's exercise
The starter code has message set to "I love pancakes", which has 15 characters. The exercise prints three things.
- The length.
message.len()gives 15. - The last character. The slice starts at
message.len() - 1, which is position 14, and runs to the end. It printss. - The last five characters. The slice starts at
message.len() - 5, which is position 10, and printscakes.
let message = "I love pancakes";
println!("{}", message.len());
println!("{}", &message[message.len() - 1..]);
println!("{}", &message[message.len() - 5..]);
15
s
cakes
Here message is a &str rather than a String, and len() and slicing work the same on both.
Gotchas
- Empty strings.
len() - 1on an empty string is the unsigned subtraction0 - 1. In a build with optimizations it wraps around to 18446744073709551615, and in a debug build it panics. Either way, the slice that follows panics because the position is out of bounds. Checkis_empty()first. - Taking more than exists.
length - 7for a string of length 5 underflows the same way. Make suren <= lengthbefore slicing. - Starting at the length.
&message[message.len()..]is legal and gives an empty string, not the last character. That is one position too far, and the next lesson looks at it. - Non-ASCII text. A byte position computed from
len()may fall inside a multi-byte character and panic. For such text, work withchars(), for examplemessage.chars().last()givesSome('d')for"Hello world".
Key takeaways
len()returns ausizeholding the number of bytes.- The last position is
len() - 1; the lastncharacters are&s[len() - n..]. - Characters and bytes differ outside ASCII; use
chars().count()for characters. - Subtracting from the length of a short or empty string is a classic source of bugs.
