carllerche / tower-web

A fast, boilerplate free, web framework for Rust
MIT License
980 stars 51 forks source link

Lifetime error in chained and_then calls #213

Closed gakonst closed 5 years ago

gakonst commented 5 years ago

I am unable to get the following piece of code to compile (minimal example based on initial issue):

#[macro_use]
extern crate tower_web;
use futures::future::{ok, Future};
use hyper::Response;

impl_web! {
    pub struct MyStruct;

    impl MyStruct {
        fn bar(&self, data: u64) -> impl Future<Item = String, Error = ()> {
            ok(data.to_string())
        }

        #[get("/")]
        fn foo(&self) -> impl Future<Item = Response<String>, Error = Response<String>> {
            ok(1)
                .and_then(move |id| {
                    let data = id; // some future that does something with the id
                    ok(data) 
                }) 
                .and_then(move |data| {
                    // FIXME: Figure out why we get lifetime errors here.
                    // `bar` MUST outlive `foo`?
                    self.bar(data).map_err(|_| {  // some function that uses the data
                        Response::builder()
                            .status(502)
                            .body("Error".to_string())
                            .unwrap()
                    })
                })
                .and_then(move |i| Ok(Response::builder().status(200).body(i).unwrap()))
        }
    }
}

Initial error is about bar potentially living less than foo:

error: cannot infer an appropriate lifetime
  --> src/lib.rs:22:27
   |
15 |           fn foo(&self) -> impl Future<Item = Response<String>, Error = Response<String>> {
   |                            -------------------------------------------------------------- this return type evaluates to the `'static` lifetime...
...
22 |                   .and_then(move |data| {
   |  ___________________________^
23 | |                     // FIXME: Figure out why we get lifetime errors here.
24 | |                     // `bar` MUST outlive `foo`?
25 | |                     // some function that uses the data
...  |
31 | |                     })
32 | |                 })
   | |_________________^ ...but this borrow...
   |
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 15:9
  --> src/lib.rs:15:9
   |
15 | /         fn foo(&self) -> impl Future<Item = Response<String>, Error = Response<String>> {
16 | |             ok(1)
17 | |                 .and_then(move |id| {
18 | |                     // some future that does something with the id
...  |
33 | |                 .and_then(move |i| Ok(Response::builder().status(200).body(i).unwrap()))
34 | |         }
   | |_________^
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 15:9
   |
15 |         fn foo(&self) -> impl Future<Item = Response<String>, Error = Response<String>> + '_ {
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Changing foo's signature to:

fn foo(&self) -> impl Future<Item = Response<String>, Error = Response<String>> + '_`
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:6:1
   |
6  | / impl_web! {
7  | |     pub struct MyStruct;
8  | |
9  | |     impl MyStruct {
...  |
35 | |     }
36 | | }
   | |_^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 2:14...
  --> src/lib.rs:6:1
   |
6  | / impl_web! {
7  | |     pub struct MyStruct;
8  | |
9  | |     impl MyStruct {
...  |
35 | |     }
36 | | }
   | |_^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:6:1
   |
6  | / impl_web! {
7  | |     pub struct MyStruct;
8  | |
9  | |     impl MyStruct {
...  |
35 | |     }
36 | | }
   | |_^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected tower_web::util::tuple::Either1<std::boxed::Box<(dyn futures::future::Future<Item = http::response::Response<std::string::String>, Error = tower_web::Error> + std::marker::Send + 'static)>>
              found tower_web::util::tuple::Either1<std::boxed::Box<dyn futures::future::Future<Item = http::response::Response<std::string::String>, Error = tower_web::Error> + std::marker::Send>>
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

I'd have expected that enforcing bar's return value lifetime to be the same as its self lifetime, would fix this:

fn bar<'a>(&'a self, data: u64) -> impl Future<Item = String, Error = ()> +'a
[dependencies]
tower-web = "0.3.7"
futures = "0.1.28"
hyper = "0.12.31"

note that I want to avoid inlining the function, as I might need to reuse it. Another option is to make bar an associated function and call it on Self, but that becomes unhandy if you want to use some of the Struct's fields (in the example there are none so it can be a tempting idea)

Are there any potential clues as to where the lifetimes should be annotated?

emschwartz commented 5 years ago

I think the problem is that by calling self.bar inside the closure you're trying to move the &self reference into the closure. That closure needs to be 'static because the Future will be executed by Tokio some time later. If MyStruct implements Clone, you could clone self and move that into the closure.

gakonst commented 5 years ago

Adding #[derive(Clone)] to MyStruct and instead of calling self.bar(...), cloning self at the beginning of foo and using that fixed it. Thanks!

lnicola commented 5 years ago

Another workaround would be to make it a static method and pass whatever fields it needs as arguments, possibly after cloning them.

But when await support lands you can probably revisit this, as borrowing self might work across awaits.