New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl Trait return value causes an over-restricted lifetime requirement #51069

Open
cuviper opened this Issue May 25, 2018 · 6 comments

Comments

Projects
None yet
4 participants
@cuviper
Copy link
Member

cuviper commented May 25, 2018

Reduced from an example given on the users forum.

This code (playground) fails to compile:

fn main() {
    let v = vec![""];
    runner(&v);
}

fn runner<'p>(v: &Vec<&'p str>) -> impl Iterator<Item = &'p str> {
    find(&vec![0], v)
}

fn find<'n, 'p, I, S>(_: &'n I, _: &Vec<&'p str>) -> impl Iterator<Item = &'p str>
where
    &'n I: IntoIterator<Item = S>,
{
    std::iter::empty()
}
error[E0597]: borrowed value does not live long enough
 --> src/main.rs:7:11
  |
7 |     find(&vec![0], v)
  |           ^^^^^^^ temporary value does not live long enough
8 | }
  | - temporary value only lives until here
  |
note: borrowed value must be valid for the lifetime 'p as defined on the function body at 6:1...
 --> src/main.rs:6:1
  |
6 | fn runner<'p>(v: &Vec<&'p str>) -> impl Iterator<Item = &'p str> {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

It seems the 'n lifetime is getting tied into the return type somehow.

It works if you make find return an explicit type:

fn find<'n, 'p, I, S>(_: &'n I, _: &Vec<&'p str>) -> std::iter::Empty<&'p str>
where
    &'n I: IntoIterator<Item = S>,
{
    std::iter::empty()
}

It also works if you instead remove the S parameter:

fn find<'n, 'p, I>(_: &'n I, _: &Vec<&'p str>) -> impl Iterator<Item = &'p str>
where
    &'n I: IntoIterator,
{
    std::iter::empty()
}
@andrewhickman

This comment has been minimized.

Copy link

andrewhickman commented Aug 4, 2018

I've also just run into this. Alternate example:

trait Future {}
impl Future for () {}

fn cache<I>(_: I) -> impl Future + 'static
where
    I: IntoIterator<Item = ()>,
{
    ()
}

fn cache_slice<'a>(glyphs: &[()]) -> impl Future + 'static {
    cache(glyphs.iter().cloned())
}

This tells me the lifetime of glyphs needs to be static.

@Nemo157

This comment has been minimized.

Copy link
Contributor

Nemo157 commented Aug 4, 2018

@andrewhickman I think that's a valid error, in the call to cache inside cache_slice I = std::iter::Cloned<std::slice::Iter<'(lifetime of glyphs), ()>>, an impl Trait return type is defined over all input type parameters, so for the impl Future returned from cache to meet its requested bound of 'static it requires I: 'static, which requires '(lifetime of glyphs): 'static.

Notice in @cuviper's example that the &'n I: IntoIterator avoids having the lifetime as part of the input type parameter I.

@andrewhickman

This comment has been minimized.

Copy link

andrewhickman commented Aug 4, 2018

Is there no way to express that the output type is independent of the input type?

@Nemo157

This comment has been minimized.

Copy link
Contributor

Nemo157 commented Aug 4, 2018

There will be with named existential types, you should even be able to have a generic existential type that depends on only some of the input type parameters, here's a playground

#![feature(existential_type)]

trait Future {}
impl Future for () {}

existential type CacheFuture: Future + 'static;

fn cache<I>(_: I) -> CacheFuture
where
    I: IntoIterator<Item = ()>,
{
    ()
}

pub fn cache_slice<'a>(glyphs: &[()]) -> impl Future + 'static {
    cache(glyphs.iter().cloned())
}
@cuviper

This comment has been minimized.

Copy link
Member Author

cuviper commented Aug 6, 2018

an impl Trait return type is defined over all input type parameters

In my example, that would include S for the impl Iterator. I suspect &'n I: IntoIterator<Item = S> is figuring out that this particular Item depends on 'n, and by equality S depends on 'n too, therefore this propagates 'n to the impl Iterator.

If that's the case, it still feels like a bug, but I'm not sure of the rules here.

@Arnavion

This comment has been minimized.

Copy link

Arnavion commented Aug 6, 2018

Duplicate of #42940

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