I have a rust generator which has a reference to a &self
, but I'm getting an error that says borrow may still be in use when generator yields
on the let mut g = t.inner()
line - specifically the reference to t
.
From what I can tell, the compiler can't guarantee t
outlives the generator, despite the lifetimes I've added. Is there a proper way to do this, or is this just a caveat of generators?
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=25a8034c25c248457c348729d9f363a6
#![feature(generators, generator_trait)]
use std::{ops::Generator, pin::Pin};
struct T(i32);
impl T {
// The nested generator and `&self` are important.
// Changing it to `self` and removing the lifetime fixes the error.
// Could elide the lifetime, but it's there for clarity.
fn inner<'a>(&'a self) -> impl Generator<(), Yield = i32, Return = ()> + Unpin + 'a {
move || {
// In the real example, the receiver type is `&mut self` so we need
// to pretend we have a reference to self in the generator.
// The real example is too large to include here.
let _ = self.0;
yield 0;
}
}
}
fn main() {
let t = T(10);
// Uncommenting this ref lets it compile for some reason?
// let t = &t;
let mut g = move || {
// On the other hand, this doesn't do anything other than moving the error
// let t = &t;
// The error is on the implicit reference to `t`
// More clear if it's changed to `let mut g = (&t).inner();`
let mut g = t.inner();
// Pretend to do something with the generator's results.
let _ = Pin::new(&mut g).resume(());
yield 0;
};
let _ = Pin::new(&mut g).resume(());
}
This is because generators are compiled to state machines that change their state when they yield
. In your example, since you move
t
within g
, the (implicit) borrow &t
that is taken with t.inner()
requires borrowing the generator. However, when you yield, you also need to mutable borrow the generator, which is not allowed.
This can be solved by not making the generator take the ownership of t
, which then requires borrowing immutably the generator to get &t
, but instead take the ownership of &t
, which is what happens when you let t = &t
.
You could also obtain the same behaviour by removing move
from the definition of g
:
let mut g = || {
// The error is on the implicit reference to `t`
// More clear if it's changed to `let mut g = (&t).inner();`
let mut g = t.inner();
// Pretend to do something with the generator's results.
let _ = Pin::new(&mut g).resume(());
yield 0;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With