Rust: Split a string to get both words and their positions

Viewed 1395

Rust has built-in function to split a string around whitespace, something like:

let mut iter = " Hello world".split_whitespace();

assert_eq!(Some("Hello"), iter.next());
assert_eq!(Some("world"), iter.next());

However, I would like a way to split the string into words, with their corresponding position in the string.

let mut iter = ??????(" Hello world");

assert_eq!(Some((1, "Hello")), iter.next());
assert_eq!(Some((7, "world")), iter.next());

I have absolutely no idea where to start, given that:

  • the built-in split and split_whitespace functions "consume" the whitespace, so I have no idea how much whitespace appears before a given element. Should I start from split(''), and somehow "group" the non whitespace together ?

  • there is a match_indices function that does something similar, but it can only look for a given string, or for a char (using a closure.)

Is there something built-in for that ? Or do I need to iterate over an std::str::Chars iterator ?

If so, how would I go from a Chars iterator at a given character, to a String that represents the next word ? Is there a safe way to return it from a function ? (the compiler has never let me do it, so far)

1 Answers

You could make use of the fact that split_whitespace() returns slices that point into the original slice, and calculate the required indices as distances of the original slice's address from that of each subslice:

fn addr_of(s: &str) -> usize {
    s.as_ptr() as usize
}

fn split_whitespace_indices(s: &str) -> impl Iterator<Item = (usize, &str)> {
    s.split_whitespace()
        .map(move |sub| (addr_of(sub) - addr_of(s), sub))
}

fn main() {
    let mut iter = split_whitespace_indices(" Hello world");

    assert_eq!(Some((1, "Hello")), iter.next());
    assert_eq!(Some((7, "world")), iter.next());
}
Related