Confusion about borrowing and lifetimes

Hello, I'm trying to make a program where i have an iterator that borrows some value from their caller. The iterator life is shorter than its caller so i would expect to be able to do this but I have been trying to get the lifetimes correct for a few days and fail to make the borrow checker happy.

I have made a short example to show what I'm trying to do:

struct Project<'a> {
    worker: Worker,
    iterator: Option<Box<dyn std::iter::Iterator<Item = String> +'a>>
}

impl <'a> Project<'a> {
    fn new(text: String) -> Project<'a> {
        Project {
            worker: Worker{text: text},
            iterator: None,
        }
    }

    fn next<'b>(&'a mut self) -> Option<String> {
        if self.iterator.is_none() {
            self.iterator = Some(self.worker.fetch());
        }
        self.iterator.as_mut().unwrap().next()
    }
}

struct Worker {
    text: String
}

impl Worker {
    fn fetch<'a>(&'a self) -> Box<dyn std::iter::Iterator<Item = String> + 'a> {
        Box::new(Iterator{ count: 3, text: &self.text })
    }
}

struct Iterator<'a> {
    count: u8,
    text: &'a String
}

impl std::iter::Iterator for Iterator<'_> {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        match self.count {
            0 => None,
            _ => {
                self.count -= 1;
                Some(self.text.clone())
            }
        }
    }
}


fn main() {
    let mut project = Project::new(String::from("Hello world"));
    println!("{:#?}", project.next());
    println!("{:#?}", project.next());
    println!("{:#?}", project.next());
    println!("{:#?}", project.next());
}

I get the following error when compiling:

error[E0597]: `project` does not live long enough
  --> src/main.rs:54:23
   |
53 |     let mut project = Project::new(String::from("Hello world"));
   |         ----------- binding `project` declared here
54 |     println!("{:#?}", project.next());
   |                       ^^^^^^^ borrowed value does not live long enough
...
58 | }
   | -
   | |
   | `project` dropped here while still borrowed
   | borrow might be used here, when `project` is dropped and runs the destructor for type `Project<'_>`

error[E0499]: cannot borrow `project` as mutable more than once at a time
  --> src/main.rs:55:23
   |
54 |     println!("{:#?}", project.next());
   |                       ------- first mutable borrow occurs here
55 |     println!("{:#?}", project.next());
   |                       ^^^^^^^
   |                       |
   |                       second mutable borrow occurs here
   |                       first borrow later used here

but I don't understand why this is wrong:

  • The iterator is only accessed from Project.next() so it should always be valid
  • self is borrowed in Project.next() but only for the duration of the call so it seems to me like this should also be valid.

Perhaps there is some annotation missing to explain to Project.worker will not be dropped before the worker itself?

&'a mut T<'a> is never what you want. Due to invariance, it makes the receiver be borrowed for the rest of its lifetime, rendering it useless.

For this reason, explicit lifetime annotations on &self and &mut self are a red flag. In your code, in fn next() (the inherent one, not the one in the Iterator impl), what makes you think it's needed? What makes you think the additional, and completely unused 'b lifetime is needed, too?

If you fix that (by removing both explicit lifetimes from the signature), then another problem is revealed:

    fn new(text: String) -> Project<'a> {
        Project {
            worker: Worker{text: text},
            iterator: None,
        }
    }

This makes no sense – the lifetime parameter 'a is realized from thin air, it can be anything! It doesn't depend on anything else, so something dubious is going on here.

A-ha! Got it:

self.iterator = Some(self.worker.fetch());

worker.fetch borrows from self, and you are trying to store it in self. That's a self-referential type, which is impossible in safe Rust. (Just think about it – what would happen to a self-reference if a referent were moved?)

I feel you are over-using references here. You managed to pack not one, but two red flags/footguns into less than 50 lines of trivial code. Everything in this code can be done by using owned values instead.

6 Likes

Thanks, I was indeed confused and trying to avoid copies and clones to much by borrowing everywhere. Having a look at the libraries I'm using pretty much all objects are protected by Arc so I can safely clone everywhere

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.