Skip to content

yield has a problem with lifetimes #45259

Closed
@manuels

Description

@manuels

When working with futures-await I ran into a problem with lifetimes. @Arnavion was able to pin it down to the implementation of generators.

I simplified the code and got rid of futures completely. My current minimal example to reproduce this bug reads:

#![feature(conservative_impl_trait, generators, generator_trait)]

use std::ops::{ Generator, GeneratorState };

struct Foo {
    i: u8,
}

impl Foo {
    fn i<'a>(&'a mut self) -> u8 {
        self.i += 1;
        self.i
    }
}

fn run<'a>(foo: &'a mut Foo) -> impl Generator<Yield = (), Return = u8> + 'a {
    move || {
        if false {
            yield
        }

        let _ = {
            loop {
                match foo.i() {
                    i if i == 0 => break i,
                    i if i == 1 => yield, // Error disappears when removing this line!
                    _ => unreachable!(),
                }
            }
        };

        {
            match foo.i() {
                i if i == 0 => i,
                _ => unreachable!(),
            }
        }
    }
}

fn main() {
    let mut foo = Foo { i: 0 };
    let mut g = run(&mut foo);
    match g.resume() {
        GeneratorState::Complete(i) => println!("{}", i),
        _ => unreachable!(),
    }
}

For some reason the compiler complains:

error[E0499]: cannot borrow `*foo` as mutable more than once at a time
  --> test.rs:24:23
   |
24 |                 match foo.i() {
   |                       ^^^ mutable borrow starts here in previous iteration of loop
...
38 |     }
   |     - mutable borrow ends here

error[E0499]: cannot borrow `*foo` as mutable more than once at a time
  --> test.rs:33:19
   |
24 |                 match foo.i() {
   |                       --- first mutable borrow occurs here
...
33 |             match foo.i() {
   |                   ^^^ second mutable borrow occurs here
...
38 |     }
   |     - first borrow ends here

error: aborting due to 2 previous errors

@Arnavion's diagnosis is

It appears to be a bug in generators arising because Foo::i() returns a value bounded by self's lifetime and there is a yield point while it's borrowed.

I tried to have a look at the rust compiler's implementation of std::ops::Generator by I don't know enough to debug this problem.
The fact that the compiler complains about the lifetime, however, is strange.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coroutinesArea: CoroutinesC-bugCategory: This is a bug.T-langRelevant to the language team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions