Lifetime problem with scan from Iterator trait

For a text scrambler I'm iterating over a vector of chars with fold and use the initial value for storing state: a tuple that gets updated for each interation value. The following code block works allright:

let alpha = vec!('e', 's', 't');
let t = vec!('A', ' ', 't', 'e', 's', 't', '!');

let r1 = t.iter()
    .fold((Vec::<char>::new(), Vec::<char>::new()), |mut acc, &c| {
        if alpha.contains(&c) {
            acc.0.push(c);
        } else {
            // acc.0 will be processed here before being pushed
            acc.1.append( &mut acc.0);
            acc.0 = Vec::<char>::new();
            acc.1.push(c)
        }
        acc
    });
println!("{}", r1.1.iter().collect::<String>());

Now I wanted to replace the fold with a scan. Here, the accumulator/aggregator is returned as on Option:

let r2 = t.iter()
    .scan((Vec::<char>::new(), Vec::<char>::new()), |mut acc, &c| {
        if alpha.contains(&c) {
            acc.0.push(c);
        } else {
            // acc.0 will be processed here before being pushed
            acc.1.append( &mut acc.0);
            acc.0 = Vec::<char>::new();
            acc.1.push(c)
        }
        Some(acc)
    }).last().unwrap();

But I fail to get it to compile:

error: lifetime may not live long enough
  --> test/src/main.rs:21:13
   |
12 | .scan((Vec::<char>::new(), Vec::<char>::new()), |mut acc, &c| {
   |                                                  -------    - return type of closure is Option<&'2 mut (Vec<char>, Vec<char>)>
   |                                                  |
   |                                                  has type `&'1 mut (Vec<char>, Vec<char>)`
...
21 |     Some(acc)
   |     ^^^^^^^^^ returning this value requires that `'1` must outlive `'2`

How is this done correctly?

PS: the full example can be found here: Text scrambler

I think you misunderstand the purpose of scan:

No, that's supposed to be the return value of the iterator itself, according to the documentation:

Accordingly, this compiles, but of course it doesn't yield the whole state. I don't think it can, either – scan is defined to take ownership of the state and then return an iterator that only yields the return values of the closure in sequence.

I think scan is not supposed to return it's accumulator. You could, using clone, but I get the feeling that is not what you are trying to do. Maybe something like

let alpha = vec!['e', 's', 't'];
let t = vec!['A', ' ', 't', 'e', 's', 't', '!'];

let mut acc = (Vec::new(), Vec::new());
t.iter().for_each(|&c| {
    if alpha.contains(&c) {
        acc.0.push(c);
    } else {
        // acc.0 will be processed here before being pushed
        acc.1.append(&mut acc.0);
        acc.0 = Vec::<char>::new();
        acc.1.push(c)
    }
});

println!("{}", acc.1.iter().collect::<String>());

would be closer to what you want to do ?

My fault - I wasn't clear: I meant the closure returns an Option (for each iteration). Since the whole scan returns an interator, I do this .last().unwrap() to get the value from the last option. It will contain the lastest state ...

I understand, but that's impossible with scan. Scan takes the initial state by-value, and consumes it. It only gives off temporary mutable references to it (while it's holding it) to the closure. Therefore, if you were allowed to yield it from the closure, it would be a dangling reference by the time scan() returned.

What you could do is only give a mutable reference to scan to begin with, and ignore the return value completely, like this.

(However, at that point, using scan is kind of pointless, as it's just an unnecessarily complicated equivalent of .for_each() or a plain for…in loop.)

1 Like

I think I get it now. It would work if I return Some((acc.0.to_vec(), acc.1.to_vec())) from the closure (with copied values). But this would create a new tuple for each iteration which also is pointless in this scenario.