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"