3

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.

ctrl-alt-delor
  • 7,506
  • 5
  • 40
  • 52
  • `&str` is `Copy`, `let first = *first;` is enough. – Chayim Friedman Aug 14 '22 at 01:06
  • @ChayimFriedman I would love an explanation in an answer. It would be worth at least an up-vote. I tried it, it worked. I think I know why. I hypothesised that `if let Some(&first)` would also work. it did, and less text. – ctrl-alt-delor Aug 14 '22 at 10:54
  • @ChayimFriedman I am still trying to get my head around it. I was wondering why original did not work. My working hypothesis is that if we don't use `first` later, then it can be freed: is not active at the same time as the other reference. This contradicts https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/references-and-borrowing.htm That states that it is freed when it goes out of scope (not after last use). However, there example of code that will not compile, is wrong. It does compile. **Have the rules been relaxed, since then?** – ctrl-alt-delor Aug 14 '22 at 11:28
  • 1
    Yes. [What are non-lexical lifetimes?](https://stackoverflow.com/questions/50251487/what-are-non-lexical-lifetimes) – Chayim Friedman Aug 14 '22 at 11:31

1 Answers1

5

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();
Stargateur
  • 24,473
  • 8
  • 65
  • 91
cdhowie
  • 158,093
  • 24
  • 286
  • 300