Skip to content

impl Generator complains about missing named lifetime if yielded expression contains a borrow #44197

Closed
@Arnavion

Description

@Arnavion

... even though the value of the expression does not borrow from anything.

rustc 1.21.0-nightly (c11f689d2 2017-08-29)
binary: rustc
commit-hash: c11f689d2475dd9ab956e881238d5d7b6b485efb
commit-date: 2017-08-29
host: x86_64-pc-windows-msvc
release: 1.21.0-nightly
LLVM version: 4.0

Case 1:

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

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

fn foo(_: &str) -> String {
    String::new()
}

fn bar(baz: String) -> impl Generator<Yield = String, Return = ()> {
    move || {
        yield foo(&baz);
    }
}

fn main() {
    assert_eq!(bar(String::new()).resume(), GeneratorState::Yielded(String::new()));
}

This gives:

error[E0564]: only named lifetimes are allowed in `impl Trait`, but `` was found in the type `[generator@src\main.rs:10:2: 12:3 baz:std::string::String ((), std::string::String, &str, fn(&str) -> std::string::String {foo})]`
 --> src\main.rs:9:24
  |
9 | fn bar(baz: String) -> impl Generator<Yield = String, Return = ()> {
  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Workaround:

-        yield foo(&baz);
+        let quux = foo(&baz);
+        yield quux;

Case 2:

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

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

fn foo(_: &str) -> Result<String, ()> {
    Err(())
}

fn bar(baz: String) -> impl Generator<Yield = String, Return = ()> {
    move || {
        if let Ok(quux) = foo(&baz) {
            yield quux;
        }
    }
}

fn main() {
    assert_eq!(bar(String::new()).resume(), GeneratorState::Complete(()));
}

This gives

error[E0564]: only named lifetimes are allowed in `impl Trait`, but `` was found in the type `[generator@src\main.rs:10:2: 14:3 baz:std::string::String ((), std::result::Result<std::string::String, ()>, &str, std::string::String, fn(&str) -> std::result::Result<std::string::String, ()> {foo})]`
 --> src\main.rs:9:24
  |
9 | fn bar(baz: String) -> impl Generator<Yield = String, Return = ()> {
  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Workaround:

-        if let Ok(quux) = foo(&baz) {
+        let quux = foo(&baz);
+        if let Ok(quux) = quux {

I assume the borrows in the expressions are the problem because in both cases, adding a + 'static bound to the impl Generator returns the error that baz does not live long enough, even though it does not need to be borrowed past the call to foo(&baz).

Activity

alexcrichton

alexcrichton commented on Aug 31, 2017

@alexcrichton
Member
alexcrichton

alexcrichton commented on Aug 31, 2017

@alexcrichton
Member

This may or may not be the same as #44200

Zoxc

Zoxc commented on Aug 31, 2017

@Zoxc
Contributor

Like #44200 this is due to how interior types are being calculated.
In both your examples &baz is considered a temporary and lives to the enclosing destruction scope, which are the yield statement and the if statement. Both of these contains a yield expression so &str is being considered part of the generator interior. Now impl Trait only allows explicitly named lifetimes in the return type so it gives an error about that &str, although poorly. You need to pass -Z verbose to the compiler for that error to be usable.

Arnavion

Arnavion commented on Aug 31, 2017

@Arnavion
Author

In both your examples &baz is considered a temporary and lives to the enclosing destruction scope

Just to be clear, you're only talking about the situation due to the bug with generators, right? Because in regular Rust the borrow of &baz is limited to the foo(&baz) expression and not any outer match / if let expression that it's part of. (Otherwise something like if let Some(first) = iter.next() { if let Some(second) = iter.next() { } } would not compile and complain about multiple mutable borrows of iter.)

Zoxc

Zoxc commented on Aug 31, 2017

@Zoxc
Contributor

@Arnavion It's not really a bug (just undesirable semantics), and you typically don't notice these rules unless the types involved have destructors (which references do not) and isn't immediately moved.

Arnavion

Arnavion commented on Aug 31, 2017

@Arnavion
Author

Okay, regardless of what we call it, it's something that happens only with generators and not with regular Rust code, so it would be nice to fix it.

added
C-bugCategory: This is a bug.
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Sep 1, 2017
pftbest

pftbest commented on Sep 5, 2017

@pftbest
Contributor

This code produces the same error:

fn multiply<G>(g: G, x: i32) -> impl Generator<Return = (), Yield = i32>
where
    G: Generator<Return = (), Yield = i32>,
{
    || while let GeneratorState::Yielded(v) = g.resume() {
        yield v * x;
    }
}

Is there some workaround?

Arnavion

Arnavion commented on Sep 5, 2017

@Arnavion
Author

@pftbest

fn multiply<G>(mut g: G, x: i32) -> impl Generator<Return = (), Yield = i32>
where
    G: Generator<Return = (), Yield = i32>
{
    move || loop {
        let state = g.resume();
        if let GeneratorState::Yielded(v) = state {
            yield v * x;
        }
        else {
            return;
        }
    }
}

If you still want to keep the while let you can do something like:

    move || while let GeneratorState::Yielded(v) = { let state = g.resume(); state } {
        yield v * x;
    }
pftbest

pftbest commented on Sep 5, 2017

@pftbest
Contributor

@Arnavion , thanks! Looks like I also hit #38615 when I forgot to add move.

25 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @Zoxc@alexcrichton@shepmaster@Arnavion@pftbest

      Issue actions

        `impl Generator` complains about missing named lifetime if yielded expression contains a borrow · Issue #44197 · rust-lang/rust