Start learning free

LESSON 252 OF 414 · TWO POINTERS

Reverse String in Rust: Two Pointers on a Vec<char>

Practice O(n) Read the full explanation

Given a string, write a function that reverses that string without using built-in functions or libraries.

Example:

Input:  "hello"

Output: "olleh"

Note:

Your algorithm should run in O(n) time and use O(n) extra space.


STEP 1 OF 8
    Ctrl + Enter to check

    RUST · TWO POINTERS

    Reverse String in Rust, explained

    Problem overview

    The function reverse takes a String called input and returns a new String with the same characters in the opposite order. For "hello" the answer is "olleh". The statement asks for O(n) time and O(n) extra space, and rules out built-in reversing helpers, so the lesson swaps characters by hand.

    Approach: swap from both ends

    Reversing means that the first character trades places with the last, the second with the second to last, and so on until the middle. Two indices do this: left starts at the first position and right at the last. Each round swaps the two characters, then moves both indices one step toward the middle. When left reaches or passes right, every pair has been swapped and the work is done. A middle character in an odd-length string stays where it is, because it is its own mirror.

    Rust adds one preliminary problem. A String is stored as UTF-8 bytes, and a single character can take one to four of them, so input[i] is not allowed. The usual fix is to copy the characters into a Vec<char>, where every element is one whole Unicode character and indexing works. That copy is the O(n) extra space the statement allows.

    Step by step

    1. Collect the characters. input.chars() is an iterator, and collect() builds a collection from it. It needs to know which collection, and the annotation Vec<char> on chars tells it.
    2. Store the length in n. It is the number of characters, not bytes, because it comes from the vector.
    3. Clone the vector into result, which is declared mut. The swaps change this copy and leave chars as it was.
    4. Set up the pointers as a tuple: left at 0usize and right at n.saturating_sub(1), the last index. Plain n - 1 breaks for an empty string, as the edge cases below show; saturating_sub stops at 0 instead.
    5. Loop while left < right. When the pointers meet or cross, nothing is left to swap.
    6. Swap. result.swap(left, right) exchanges the two elements without a temporary variable.
    7. Move the pointers with left += 1 and right -= 1. The subtraction is safe: the loop only runs when left < right, so right is at least 1.
    8. Build the answer. result.into_iter().collect() turns the vector back into a String. Nothing tells collect the target type here except the function's return type, and that is enough. The line has no semicolon, so it is the value the function returns.

    The solution in Rust

    impl Solution {
        fn reverse(input: String) -> String {
            let chars: Vec<char> = input.chars().collect();
            let n = chars.len();
            let mut result = chars.clone();
            let (mut left, mut right) = (0usize, n.saturating_sub(1));
            while left < right {
                result.swap(left, right);
                left += 1;
                right -= 1;
            }
            result.into_iter().collect()
        }
    }
    

    Rust details worth knowing:

    • collect() is generic. The same method can build a Vec, a String, a HashMap and more. The compiler picks from the type it expects, which comes from an annotation or from the return type. With neither, it reports that it can't infer the type.
    • into_iter() consumes the vector. After the call, result can't be used. That is fine here because it is the last use, and no elements are copied.
    • Characters are not bytes. "héllo".len() is 6, since é takes two bytes, but "héllo".chars().count() is 5. Reversing a Vec<char> keeps é in one piece, and "héllo wörld" becomes "dlröw olléh".
    • A char is a Unicode scalar value, not always what a reader sees as one letter. A letter written as e followed by a combining accent is two chars, and reversing puts the accent before the e. For the lesson's plain text this doesn't matter.
    • Tuple destructuring. let (mut left, mut right) = (...) declares two variables at once, and each one gets its own mut.

    Complexity

    Time: O(n). Collecting the characters, cloning, the swap loop (about n/2 rounds) and collecting the result each touch every character a constant number of times.

    Space: O(n). The code holds chars, its clone result and the final string, which is three copies of size n, still linear. Declaring chars as mut and swapping it directly would avoid the clone and keep a single vector, but the lesson's version is easier to follow because the original stays untouched.

    Edge cases and common mistakes

    • Empty string. There is nothing to swap. With n.saturating_sub(1) the right pointer is 0, the loop condition 0 < 0 is false, and the result is an empty string. With plain n - 1 and optimizations on, 0 minus 1 wraps to the largest usize; the loop starts and swap panics with "index out of bounds: the len is 0". In a debug build the subtraction itself panics.
    • One character. left and right are both 0, the loop doesn't run, and the string comes back unchanged.
    • Even and odd lengths. "abcd" swaps two pairs and gives "dcba". In an odd-length string the loop stops with both pointers on the middle character, which stays in place.
    • Using <= in the loop condition. When both pointers meet on the middle, the extra round would swap a character with itself and then move right below left. For left = right = 0, right -= 1 underflows. Keep the strict <.
    • Trying to index the string. input[0] doesn't compile, and slicing with byte ranges can panic in the middle of a multi-byte character. Work on the Vec<char> instead.

    Testing

    • "hello" gives "olleh".
    • "abcd" gives "dcba".
    • "a" gives "a".
    • "" gives "".
    • "héllo wörld" gives "dlröw olléh".
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free