Help regarding lifetimes with iter::repeat_with(...)

use std::iter;
use std::cell::RefCell;

#[derive(Debug)]
struct NoClone(usize);

fn main() {
    let mut foo = NoClone(0);
    {
        for mut bar in iter::repeat_with(|| &mut foo).take(1) {
            bar.0 += 1;
        }
    }
    
    println!("{:?}", foo);
}

fn work_around() {
    let mut foo = NoClone(0);
    {
        let bar = RefCell::new(&mut foo);
        for mut baz in iter::repeat_with(|| bar.borrow_mut()).take(1) {
            baz.0 += 1;
        }
    }
    
    println!("{:?}", foo);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:10:45
   |
10 |         for mut bar in iter::repeat_with(|| &mut foo).take(1) {
   |                                             ^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 10:42...
  --> src/main.rs:10:42
   |
10 |         for mut bar in iter::repeat_with(|| &mut foo).take(1) {
   |                                          ^^^^^^^^^^^
note: ...so that closure can access `foo`
  --> src/main.rs:10:45
   |
10 |         for mut bar in iter::repeat_with(|| &mut foo).take(1) {
   |                                             ^^^^^^^^
note: but, the lifetime must be valid for the expression at 10:24...
  --> src/main.rs:10:24
   |
10 |         for mut bar in iter::repeat_with(|| &mut foo).take(1) {
   |                        ^^^^^^^^^^^^^^^^^
note: ...so type `fn([closure@src/main.rs:10:42: 10:53 foo:&mut NoClone]) -> std::iter::RepeatWith<[closure@src/main.rs:10:42: 10:53 foo:&mut NoClone]> {std::iter::repeat_with::<&mut NoClone, [closure@src/main.rs:10:42: 10:53 foo:&mut NoClone]>}` of expression is valid during the expression
  --> src/main.rs:10:24
   |
10 |         for mut bar in iter::repeat_with(|| &mut foo).take(1) {
   |                        ^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Is it possible to solve the lifetime issues without using the work_around which comes with a runtime cost?

If it was possible to make this compile, you could just as easily do .take(2).collect() instead and get two aliasing references to foo, which is UB.

3 Likes

Ah ok this makes sense. So i have to use the work_around or do some unsafe code and make sure im not using something like collect.