Problem overview
The function reverse takes a String called input and returns a new String with the characters in the opposite order. For "hello" the result is "olleh". This version of the lesson asks for O(n) time and O(n) extra space, where n is the length of the string.
Approach: read backwards, append forwards
One way to reverse a string is to build the result by putting each character in front of the others. That works, but inserting at the front of a String shifts every byte already stored, so the total work grows as O(n^2).
The faster way is to flip the roles. Read the input from its last character to its first, and add each character to the end of the result. Adding to the end of a String doesn't move anything that is already there; it takes amortized constant time. A loop of n constant-time appends costs O(n).
The only difficulty is reading backwards. Rust strings can't be indexed by position, since characters have different byte lengths in UTF-8, so counting an index down from the end is not an option. The character iterator solves this: chars() walks forwards, and rev() turns it around so it walks from the end.
Step by step
- Create the result with room reserved.
String::with_capacity(input.len())makes an emptyStringwhose buffer can already hold as many bytes as the input has. The reversed text uses exactly the same bytes, so the buffer never has to be reallocated while it grows. The variable ismutbecause it changes, and the lesson names itreversed_string. - Loop over the characters backwards.
input.chars().rev()produces the characters from last to first. Name the loop variablec. - Append each character with
push(c), which adds onecharto the end of theString. - Return
reversed_stringas the last expression, with noreturnkeyword and no semicolon.
On "hello" the loop sees o, l, l, e, h in that order, and appending them in turn produces "olleh".
The solution in Rust
impl Solution {
fn reverse(input: String) -> String {
let mut reversed_string = String::with_capacity(input.len());
for c in input.chars().rev() {
reversed_string.push(c);
}
reversed_string
}
}
Rust details worth knowing:
- Capacity is measured in bytes.
input.len()is the number of bytes, not characters. That is the right quantity here, because reversing characters keeps the total byte count the same. Capacity only avoids reallocations. It doesn't change the contents, andString::new()would give the same result, a little more slowly for long inputs. rev()works onchars(). The iterator can be walked from either end, so Rust can reverse it without first collecting the characters into a vector.- Whole characters move. Since
chars()yields completecharvalues, text such as"héllo"becomes"olléh", with the two-byteéintact. pushagainstpush_str.pushappends onechar, andpush_strappends a string slice. Here each loop step produces a singlechar, sopushis the right method.- The tail expression. The final line has no semicolon, so its value is the function's result.
The whole body can also be written as one expression, input.chars().rev().collect(), with the return type telling collect to build a String. The loop above is the same idea written out step by step.
Complexity
Time: O(n). The loop runs once per character, and each push is amortized constant time. Because the capacity was reserved up front, no reallocation happens at all, though the bound holds without that.
Space: O(n). The result is a second string as long as the input. The iterator itself uses constant extra space.
Edge cases and common mistakes
- Empty string. The loop doesn't run, and the function returns
"". - One character.
"a"gives"a". - Palindromes.
"racecar"comes back unchanged. - Spaces.
"ab cd"gives"dc ba", since a space is a character like any other. - Forgetting
.rev(). Looping overinput.chars()and pushing returns an exact copy. - Forgetting
mut. Without it,pushdoesn't compile. - Sizing by characters. Using
input.chars().count()as the capacity would reserve too little for non-ASCII text. Uselen(), which counts bytes.
Testing
"hello"gives"olleh".""gives""."a"gives"a"."ab cd"gives"dc ba"."héllo"gives"olléh".
