How to reverse a string
Short answer: String and str have no reverse() method — write s.chars().rev().collect::<String>() to reverse by characters. The method is missing because a String is a buffer of UTF-8 bytes, and reversing those bytes produces text that isn’t valid UTF-8, which a String may not hold. Vec has reverse() because its elements are fixed-size values, where “backwards” is unambiguous.
From a fresh project — cargo new reverse-demo, then replace src/main.rs with each block below and run it with cargo run. The project name only shows up in cargo’s own output; nothing else on this page depends on it.
Reverse by characters
Section titled “Reverse by characters”fn main() { for text in ["hello", "café", "😀hi"] { let reversed: String = text.chars().rev().collect(); println!("{text} -> {reversed}"); }}hello -> ollehcafé -> éfac😀hi -> ih😀chars() walks the characters one at a time — the iteration article covers the walk — and the Chars iterator is double-ended, so .rev() walks it from the end instead. String implements FromIterator<char>, which is what turns the characters back into text; the iteration article meets the same collector from the other direction. One pass over the input, one String out.
Why there’s no reverse()
Section titled “Why there’s no reverse()”Vec has the method. Every element is the same size, so reversing is a matter of swapping fixed-size slots:
fn main() { let mut nums = vec![1, 2, 3]; nums.reverse(); println!("{nums:?}");}[3, 2, 1]A String is a Vec<u8> whose contents have to stay valid UTF-8, and a character is one to four bytes — the fact that rules out s[0], as the previous article shows. Reverse the bytes of “café” and the é — 0xC3 0xA9 — comes out as 0xA9 0xC3, a continuation byte leading, which is not a character and not valid UTF-8. A method that did this could not return a String:
fn main() { let text = "café"; let mut bytes = text.as_bytes().to_vec(); bytes.reverse(); println!("{bytes:?}"); match String::from_utf8(bytes) { Ok(s) => println!("{s}"), Err(e) => println!("{e}"), }}[169, 195, 102, 97, 99]invalid utf-8 sequence of 1 bytes from index 0from_utf8 checks the bytes as it converts and hands back the error instead of the corrupted text; the conversion itself gets its own article later in this chapter. Asking for the missing method directly gets the compiler’s version of the same answer:
fn main() { let text = String::from("café"); let reversed = text.reverse(); println!("{reversed}");}error[E0599]: no method named `reverse` found for struct `String` in the current scope --> src/main.rs:3:25 |3 | let reversed = text.reverse(); | ^^^^^^^ |help: there is a method `reserve` with a similar name, but with different arguments --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/alloc/src/string.rs:1235:4
For more information about this error, try `rustc --explain E0599`.The help line offers reserve, which is about capacity, not order — the compiler matched the name, not the meaning. Nothing in the standard library reverses text for you, so the level to reverse at is the reader’s choice.
Characters aren’t letters either
Section titled “Characters aren’t letters either”Reversing by char splits some visible letters in two. An “e” followed by a combining acute accent prints as one letter and is two chars — the iteration article makes the same point about counting. Reversed, the accent comes first:
fn main() { let text = "e\u{301}"; println!( "{:?} is {} chars and {} bytes", text, text.chars().count(), text.len() ); let reversed: String = text.chars().rev().collect(); println!("{reversed:?}"); println!("equal: {}", text == reversed);}"e\u{301}" is 2 chars and 3 bytes"\u{301}e"equal: falseThe escapes make the difference plain: in the reversed string the accent comes before the letter. The two are different strings — == says so on the last line — and a search for the original won’t match the reversed text. When “reversed” has to mean “one visible letter stays one letter”, the unit is the grapheme cluster, and the standard library doesn’t cut text that way: the unicode-segmentation crate does, with graphemes(true) walking clusters the way chars() walks scalars. For the string above it counts one cluster, and reversing by clusters returns the same string.
Words, if words are what you mean
Section titled “Words, if words are what you mean”fn main() { let text = "Ada Lovelace"; let words: Vec<&str> = text.split_whitespace().rev().collect(); println!("{}", words.join(" "));}Lovelace Adasplit_whitespace() hands out the pieces — the splitting article covers it — its iterator is double-ended too, and join puts the spaces back. Reversing words leaves the characters inside each word in order, which is often what “reverse the sentence” means in practice.
Related
Section titled “Related”- How to iterate over the characters of a string —
chars(),bytes(), and what acharis - Why can’t you index into a String? — the variable-width fact behind the missing method
- What is the difference between String and &str? — the UTF-8 buffer underneath
- Working with text — the rest of the chapter