[SOLVED] Trouble with named lifetimes in chained futures

Hi everybody,
I'm trying to understand this error message: only named lifetimes are allowed in `impl Trait`, but `` was found in the type.

What I want to do is to chain two futures (using and_then) so my fut1 function implements part of the logic.fut2 then picks up from fut1 and adds more functionalities. Everything works fine unless I pass a lifetime-bound struct in the parameters: the first function works but the second one gives the above error.

Here is the repro. This code works (boxing the futures):

extern crate tokio_core;
use tokio_core::reactor::Core;

extern crate futures;
use futures::future::*;

#[derive(Debug)]
struct MyStruct<'a> {
    s: &'a str,
}

fn func<'a>(a: &MyStruct<'a>) -> Result<String, ()> {
    println!("func called {:?}", a);
    Ok(a.s.to_owned())
}

fn fut1<'a>(a: &MyStruct<'a>) -> Box<Future<Item = (), Error = ()>> {
    Box::new(done(func(a)).and_then(|_| ok(())))
}

fn fut2<'a>(a: &MyStruct<'a>) -> Box<Future<Item = (), Error = ()>> {
    Box::new(fut1(a).and_then(|_| {
        println!("chained!");
        ok(())
    }))
}

fn main() {
    let ms = MyStruct { s: "str" };

    let fut1 = fut1(&ms);
    let fut2 = fut2(&ms);

    let mut core = Core::new().unwrap();

    core.run(fut1).unwrap();
    core.run(fut2).unwrap();
}

This code does not (it's just the previous code with impl Future instead of Box<Future>:

#![feature(conservative_impl_trait)]

extern crate tokio_core;
use tokio_core::reactor::Core;

extern crate futures;
use futures::future::*;

#[derive(Debug)]
struct MyStruct<'a> {
    s: &'a str,
}

fn func<'a>(a: &MyStruct<'a>) -> Result<String, ()> {
    println!("func called {:?}", a);
    Ok(a.s.to_owned())
}

fn fut1<'a>(a: &MyStruct<'a>) -> impl Future<Item = (), Error = ()> {
    done(func(a)).and_then(|_| ok(()))
}

fn fut2<'a>(a: &MyStruct<'a>) -> impl Future<Item = (), Error = ()> {
    fut1(a).and_then(|_| {
        println!("chained!");
        ok(())
    })
}

fn main() {
    let ms = MyStruct { s: "str" };

    let fut1 = fut1(&ms);
    let fut2 = fut2(&ms);

    let mut core = Core::new().unwrap();

    core.run(fut1).unwrap();
    core.run(fut2).unwrap();
}

The error given is

   Compiling tst_fut v0.1.0 (file:///home/user/src/rust/tst_fut)
error[E0564]: only named lifetimes are allowed in `impl Trait`, but `` was found in the type `futures::AndThen<impl futures::Future, futures::FutureResult<(), ()>, [closure@src/main.rs:24:22: 27:6]>`
  --> src/main.rs:23:34
   |
23 | fn fut2<'a>(a: &MyStruct<'a>) -> impl Future<Item = (), Error = ()> {
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

How can I solve this problem?
I know I can replicate the whole code in fut2 and it will work (instead of calling the first future returning function) but I prefer not to copy-paste my code around.

Thank you in in advance,
Francesco

1 Like

I'm not familiar with the rules of impl Trait, but from what I understand of the compiler's error message, it seems that the impl Trait syntax does not allow you to use lifetime elision, and you need to explicitly spell out what is the lifetime of the object returned by fut2().

For example, if that future has the same lifetime as the input parameter "a", something like this would probably work (not sure about the syntax, just stole it from an impl Trait RFC):

fn fut2<'a>(a: &MyStruct<'a>) -> impl Future<Item = (), Error = ()> + 'a

There are multiple bug reports about this error message not being very helpful for diagnosing the issue whenever closures are involved, and about its conclusions seeming sometimes plain wrong, it seems to need more work.

2 Likes

Remove the lifetime specifier from fut1 (Can also remove from fut2.)

Alternatively change fut2 to get;

warning: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
--> src/main.rs:24:12
|
24 | fut1::<'a>(a).and_then(|_| {
| ^^
|
= note: #[warn(late_bound_lifetime_arguments)] on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #42868 https://github.com/rust-lang/rust/issues/42868

There is a link explaining late bound in detail.

1 Like

I see! I've added the extra constraining lifetime and it compiles now.

Thank you very much for your help! :hugs:

1 Like