Get first and all of an iterator. In Rust

Viewed 121

What I am trying to do

I have in iterator returned from std::str::SplitWhitespace, I need the first element, and a vector of all elements.

What I have tried

I tried to use peek. However this seems to need a mutable (I can't we why), and I end up with borrowing errors.

Simplified code, with compile errors.

fn main(){
    let line = "hello, world";
    let mut tokens = line.split_whitespace().peekable();
    if let Some(first) = tokens.peek() {
        //println!("{first}"); //works
        //println!("{tokens:?}"); // works
        println!("{first}\n{tokens:?}"); //compile error
    }
}
error[E0502]: cannot borrow `tokens` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:29
  |
4 |     if let Some(first) = tokens.peek() {
  |                          ------------- mutable borrow occurs here
...
7 |         println!("{first}\n{tokens:?}"); //error
  |         --------------------^^^^^^-----
  |         |                   |
  |         |                   immutable borrow occurs here
  |         mutable borrow later used here

If I un-comment the two printlns, and comment the erroneous one. then It works. This lead me to adding a clone let first = first.clone();, before the printlns. This fixed it, but I am wondering if there is a better way.

1 Answers

The simplest way to achieve this is to just collect the vector, then get the first element:

let tokens: Vec<_> = line.split_whitespace().collect();
if let Some(first) = tokens.first() {
    println!("{first}\n{tokens:?}");
}

Note the type of tokens will be Vec<&str> -- a vector of string slices that are borrowed from line. If you instead want a vector of owned strings (Vec<String>), then you need to map each element to an owned string:

let tokens: Vec<_> = line.split_whitespace().map(|s| s.to_owned()).collect();
Related