Start learning free

LESSON 255 OF 414 · TWO POINTERS

Reverse Words in Rust: Reverse the String, Then Each Word

Practice O(n) Read the full explanation

Given a string input, reverse the order of the words.

A word is defined as a sequence of non-space characters. The words in input will be separated by one space for simplicity.

Return a string of the words in reverse order concatenated by a single space.

Example:

Input:  input = "sky is blue"

Output:  "blue is sky"
STEP 1 OF 15
    Ctrl + Enter to check

    RUST · TWO POINTERS

    Reverse Words in Rust, explained

    Problem overview

    The function reverse_words takes a String called input and returns its words in the opposite order. A word is a run of non-space characters, and words are separated by one space. For "sky is blue" the answer is "blue is sky".

    Approach: two reversals

    A natural idea is to split the string into words, reverse the list and join it again. This lesson uses a different trick that works directly on the characters, with only two kinds of reversal:

    1. Reverse the whole string. "sky is blue" becomes "eulb si yks". The words are now in the right order, but each one is spelled backwards.
    2. Reverse each word on its own. "eulb" becomes "blue", "si" becomes "is" and "yks" becomes "sky".

    Both steps use one helper, reverse_range, which reverses a part of the vector in place with two pointers that swap and move toward each other. The only real work in the main function is finding where each word starts and ends: after the whole-string reversal, an outer loop walks to the start of a word, and an inner loop scans forward to the next space.

    Step by step

    1. Collect the characters. A Rust string can't be indexed or edited by character, so input.chars().collect() builds a mutable Vec<char> named chars. The annotation tells collect what to build.
    2. Handle the empty string. Store the length in n. If it is 0, return input at once: the next step needs n - 1, and a usize can't go below zero.
    3. Reverse everything. Call Solution::reverse_range(&mut chars, 0, n - 1). The helper reverses the inclusive range from index 0 to index n - 1.
    4. Track the word start in a mutable i, beginning at 0.
    5. Loop over words with while i < n, one pass per word.
    6. Find the end. Declare j equal to i. It scans forward.
    7. Scan. An inner loop runs while j < n and chars[j] is not a space. The bound check comes first, so chars[j] is never read past the end.
    8. Advance j with j += 1 inside that loop. Rust has no ++.
    9. Fix the word. The word occupies indices i to j - 1. If j > i (the word isn't empty), call reverse_range on that range. The condition also keeps j - 1 from underflowing.
    10. Skip the space with i = j + 1, so the next pass starts at the next word. After the last word, j equals n, so i goes past n and the loop ends.
    11. Return the string. chars.iter().collect() is the final expression. It gathers the characters into a String because that is the function's return type.
    12. Write the helper above reverse_words, with parameters chars: &mut Vec<char>, mut i: usize and mut j: usize. The mut allows the loop to move the indices.
    13. Loop while i < j.
    14. Swap chars[i] and chars[j] with the vector's swap method.
    15. Move both indices inward: i += 1 and j -= 1.

    The solution in Rust

    impl Solution {
        fn reverse_range(chars: &mut Vec<char>, mut i: usize, mut j: usize) {
            while i < j {
                chars.swap(i, j);
                i += 1;
                j -= 1;
            }
        }
    
        fn reverse_words(input: String) -> String {
            let mut chars: Vec<char> = input.chars().collect();
            let n = chars.len();
            if n == 0 {
                return input;
            }
            Solution::reverse_range(&mut chars, 0, n - 1);
            let mut i = 0;
            while i < n {
                let mut j = i;
                while j < n && chars[j] != ' ' {
                    j += 1;
                }
                if j > i {
                    Solution::reverse_range(&mut chars, i, j - 1);
                }
                i = j + 1;
            }
            chars.iter().collect()
        }
    }
    

    Rust details worth knowing:

    • &mut Vec<char>. Passing &mut chars lends the vector to the helper for the duration of the call. The helper can change it, and chars is usable again afterwards. The variable must be declared let mut for this to compile.
    • mut in a parameter list. mut i: usize lets the function change its own copy of the index. It doesn't make the caller's value change.
    • Why j -= 1 is safe. The loop only runs when i < j, so j is at least 1 when the subtraction happens.
    • i infers usize. let mut i = 0; has no suffix, but i < n and passing it to the helper fix its type.
    • Collecting &char into a String. chars.iter() yields references to characters, and String can be built from those directly, so no .cloned() is needed.
    • Characters, not bytes. Working on Vec<char> means accented letters stay intact inside words.

    Complexity

    Time: O(n). The whole-string reversal touches each character once. The word loop scans each character once, and reversing a word of length k costs about k/2 swaps, so all the word reversals together are linear. Collecting the characters and building the result are linear too.

    Space: O(n). The Vec<char> and the output string each hold n characters (a char takes 4 bytes). The reversals themselves use no more memory than the vector.

    Edge cases and common mistakes

    • Empty string. The early return handles it. Without it, n - 1 wraps to a huge number in an optimized build (and panics in a debug build), and the helper then indexes out of range.
    • One word. "hello" is reversed to "olleh", then the single word is reversed back, so the result is "hello".
    • Spaces are mirrored, not collapsed. The statement promises single spaces. If the input had extra ones, the whole-string reversal would still move them to mirrored positions, and no space would be removed: "a b" gives "b a", and a leading space becomes a trailing one.
    • Skipping the second step. Reversing only the whole string gives "eulb si yks". Each word has to be reversed again.
    • Reading before checking the bound. Writing the scan condition as chars[j] != ' ' && j < n reads chars[n] at the end of the last word, and Rust panics. The bound check goes first.
    • The range end. The word ends at j - 1, because j stops on the space, or at n. Passing j would pull the space, or a position out of range, into the reversal.

    Testing

    • "sky is blue" gives "blue is sky".
    • "one two three four" gives "four three two one".
    • "a b" gives "b a".
    • "hello" gives "hello".
    • "" gives "".
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free