Search code examples
dictionaryrustflat

flat_map dropped here while still borrowed


I'm trying to write a vector with the index of the string and each character in the string.

In the example below, 0 J, 0 a, 0 n ... 1 J, 1i, ...

fn main() {
    let names = vec!["Jane", "Jill", "Jack", "Johne"];
    let name3 = names.iter().enumerate().flat_map(|(i, name)| {
        let letters = name.chars();
        let mut is = Vec::new();
        for _k in 0..name.len() {
            is.push(i);
        }
        is.iter().zip(letters).collect::<Vec<_>>()
    });
    for i in name3 {
        println!("{:?}", i);
    }
}

This gives me the error

error[E0597]: `is` does not live long enough
  --> src/main.rs:9:9
   |
9  |         is.iter().zip(letters).collect::<Vec<_>>()
   |         ^^ borrowed value does not live long enough
10 |     });
   |     - `is` dropped here while still borrowed
...
14 | }
   | - borrowed value needs to live until here

I don't understand what is going on here. I've already collected the is value.

Weirdly enough, if I flip letters and is, it works.

letters.zip(is)

Solution

  • Calling is.iter() returns references to the values inside is. Your final iterator type is attempting to return these references, except that the Vec holding the values has already been deallocated.

    The simplest fix is to switch to into_iter, which takes ownership of the Vec and all its values. The more efficient fix is to avoid creating a Vec at all:

    fn main() {
        let names = vec!["Jane", "Jill", "Jack", "Johne"];
    
        let name3 = names
            .iter()
            .enumerate()
            .flat_map(|(i, name)| name.chars().map(move |c| (i, c)));
    
        for i in name3 {
            println!("{:?}", i);
        }
    }
    

    if I flip letters and is, it works.

    Yes, zip takes values that implement IntoIterator. By flipping the arguments, you end up implicitly calling into_iter on the Vec. You'd get the same undesired behavior if you did letters.zip(&is).