Start learning free

LESSON 158 OF 414 · LOOPING OVER ARRAY INDICES

Get Full Names in Rust: Building Strings with format! in a Loop

Practice Read the full explanation

Given two arrays of strings, the first names and the last names (given in this order, called firstNames and lastNames in the example), return an array containing the full names (separated by one space)

Example:

Input:   firstNames = ["John","Andy","Mary"]
         lastNames = ["Doe","Smith","Johnson"]

Output:  ["John Doe", "Andy Smith", "Mary Johnson"]
STEP 1 OF 5
    Ctrl + Enter to check

    RUST · LOOPING OVER ARRAY INDICES

    Get Full Names in Rust, explained

    Problem overview

    The function get_full_names receives two vectors of strings, first and last. The name at position i in first belongs with the name at position i in last. The function returns a new vector of full names, each made of the first name, one space and the last name, in the original order.

    For first = ["John", "Andy", "Mary"] and last = ["Doe", "Smith", "Johnson"], the result is ["John Doe", "Andy Smith", "Mary Johnson"].

    Approach: walk both vectors with one index

    The two vectors line up by position, so a single index can step through both at once. At each index you build one string from first[i] and last[i] and add it to a result vector. The result starts empty and grows by one name per iteration, so after the last index it holds every full name in the same order as the inputs.

    You can't loop directly over the elements of both vectors with a plain for name in &first, because you would have no way to reach the matching last name. An index solves this: it names the same position in both vectors.

    Step by step

    1. Create the result. Declare let mut result = Vec::new();. It needs mut because you will push into it. You don't write the element type: Rust works out from the later push and the return type that it is a Vec<String>.
    2. Loop over the indices of first with for i in 0..first.len(). The range stops before len(), so it covers exactly the valid indices.
    3. Build the full name. Inside the loop, format!("{} {}", first[i], last[i]) creates a new String. Each {} is filled by the next argument, and the space between the braces is the single space you need between the names. Bind the result to name.
    4. Push it. result.push(name) moves name into the vector, at the end.
    5. Return result once the loop finishes.

    The solution in Rust

    impl Solution {
        fn get_full_names(first: Vec<String>, last: Vec<String>) -> Vec<String> {
            let mut result = Vec::new();
            for i in 0..first.len() {
                let name = format!("{} {}", first[i], last[i]);
                result.push(name);
            }
            return result;
        }
    }
    

    The Rust details worth a closer look:

    • format! versus println!. Both use the same template syntax. println! prints the text, while format! returns it as a new String.
    • format! only borrows its arguments. Writing first[i] inside it doesn't move the string out of the vector. That matters because moving out of an indexed Vec<String> isn't allowed: let s = first[0]; fails with "cannot move out of index of Vec<String>", because String doesn't implement Copy. Inside format! the vector keeps its strings.
    • Type inference for Vec::new(). The compiler has no element type when it sees Vec::new(). It fills it in from the push of a String and from the function's return type. If nothing ever constrained it, you would get an error asking for a type annotation.
    • Owned strings. The parameters are Vec<String>, not Vec<&str>. Each element owns its text, and the function creates new owned strings for the result.
    • Pairing with iterators. The idiomatic alternative walks both vectors together with first.iter().zip(last.iter()) and builds the result with map and collect. The index loop makes the pairing explicit, which suits this lesson.

    Complexity

    Let n be the number of names and m the length of the longest name.

    Time: O(n * m). The loop runs n times. Each iteration builds a string by copying the two names, which costs time proportional to their lengths. If the names are treated as short and bounded, this is simply O(n).

    Space: O(n * m). The result holds n new strings, each about as long as a first and a last name together. Apart from the result, the function uses only the loop variable.

    Edge cases and common mistakes

    • Empty vectors. Both empty gives an empty result, since the loop never runs.
    • Different lengths. The loop runs over first.len(). If last is shorter, last[i] panics with "index out of bounds". The lesson assumes both vectors have the same length. Looping up to the shorter length would be the safe choice for general code.
    • The space. format!("{}{}", ...) without a space would give JohnDoe. The required output has exactly one space between the names and none at the ends.
    • Moving out of the vector. Writing let f = first[i]; doesn't compile for strings. Borrow with &first[i] or let format! do it.
    • Forgetting mut. Calling push on an immutable vector is a compile error.
    • Returning inside the loop. That would stop after the first name. The return goes after the loop.

    Testing

    • first = ["John", "Andy", "Mary"], last = ["Doe", "Smith", "Johnson"] gives ["John Doe", "Andy Smith", "Mary Johnson"].
    • first = ["Ada"], last = ["Lovelace"] gives ["Ada Lovelace"].
    • Two empty vectors give an empty vector.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free