Split a string into several individual variables

itertools splitn next into_iter

Sometimes we have string that contains several values separated by a delimiter. Similar to a line of a CSV file, but the separator isn't necessarily a comma.

For example a string that looks like this: "Foo:Bar:42". How can we split it up and assign the parts to individual variables?

We have several solutions here using the splitn method that allows us to tell rust exactly how many part we are interested in.

This splitn returns an iterator we can either use directly or collect its values to a vector and then use that.

Each solution has some advantages and some problems.

splitn_and_next uses the iterator directly calling the next method several times. The problem with this solution is that if there are not enough parts in the original string, then there won't be enough iterations and this code will panic!.

splitn_and_collect collects the parts into a vector. This allows us to check if the number of parts is as expected.

splitn_itertools only needs to collect the parts into a vector in order to verify the correct number of parts. Then we convert it back to an iterator and assign the values to the variables.

examples/split-string-into-many-variables/src/main.rs

fn main() {
    for text in ["Foo:Bar:42", "Foo:Bar:", "Foo:Bar:42:garbage", "Foo:Bar"] {
        println!("str: {text}");
        splitn_and_collect(text);
        splitn_itertools(text);
        splitn_and_next(text);
    }
}

// The call to `next` will panic! if there are not enough parts
fn splitn_and_next(text: &str) {
    println!("str: {text}");
    let mut parts = text.splitn(3, ':');

    let fname = parts.next().unwrap();
    let lname = parts.next().unwrap();
    let value = parts.next().unwrap();
    println!("fname: {fname}");
    println!("lname: {lname}");
    println!("value: {value}");
    println!();
}

fn splitn_and_collect(text: &str) {
    let parts = text.splitn(3, ':').collect::<Vec<&str>>();
    if parts.len() != 3 {
        eprintln!("Not enough parts");
        return;
    }

    let (fname, lname, value) = (parts[0], parts[1], parts[2]);
    println!("fname: {fname}");
    println!("lname: {lname}");
    println!("value: {value}");
    println!();
}

fn splitn_itertools(text: &str) {
    use itertools::Itertools;
    let parts = text.splitn(3, ':').collect::<Vec<&str>>();
    if parts.len() != 3 {
        eprintln!("Not enough parts");
        return;
    }

    if let Some((fname, lname, value)) = parts.into_iter().tuples().next() {
        println!("fname: {fname}");
        println!("lname: {lname}");
        println!("value: {value}");
    }
    println!();
}

Using itertools

In order to be able to collect the elements from an iterator in a single call we need the itertools crate.

To add it to the crate we are working on type cargo add itertools or edit the Cargo.toml file and add it manually under the dependencies section:

examples/split-string-into-many-variables/Cargo.toml

[package]
name = "split-string-into-many-variables"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
itertools = "0.12.0"

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo