0

I'm trying to implement an HTTP layer on top of RocksDB as a toy project. My idea was to create an endpoint which returns a "never-ending" stream of data which is finished when client closes connection.

I need the lifetime of a variable to last as long as the lifetime of the HTTP request: Rust playground

extern crate hyper;
extern crate tokio;

use std::convert::Infallible;
use std::net::SocketAddr;

use bytes::Bytes;
use futures::Stream;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};

async fn hello_world(_: Request<Body>) -> Result<Response<Body>, Infallible> {
    let add_underscore = "_";
    let iter = vec!["hello", "world"]
        .into_iter()
        .map(|x| format!("{}{}", x, add_underscore))
        .map(|x| Ok(Bytes::from(x)));

    let stream: Box<dyn Stream<Item=Result<Bytes, Box<dyn std::error::Error + Send + Sync>>> + Send + Sync> 
        = Box::new(futures::stream::iter(iter));

    let response = http::Response::builder()
        .header(
            "Content-Type",
            "application/octet-stream",
        )
        .body(Body::from(stream))
        .unwrap();

    Ok(response)
}

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    let make_svc = make_service_fn(|_conn| async {
        Ok::<_, Infallible>(service_fn(hello_world))
    });

    let server = Server::bind(&addr).serve(make_svc);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}
error[E0597]: `add_underscore` does not live long enough
  --> src/main.rs:16:37
   |
16 |         .map(|x| format!("{}{}", x, add_underscore))
   |              ---                    ^^^^^^^^^^^^^^ borrowed value does not live long enough
   |              |
   |              value captured here
...
19 |     let stream: Box<dyn Stream<Item=Result<Bytes, Box<dyn std::error::Error + Send + Sync>>> + Send + Sync> = Box::new(futures::stream::iter(iter));
   |                                                                                                               ------------------------------------- cast requires that `add_underscore` is borrowed for `'static`
...
29 | }
   | - `add_underscore` dropped here while still borrowed

If you run the code locally and comment the 2 lines with add_underscore you get a compiling version:

curl -XGET -v --silent 127.0.0.1:3000
*   Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:3000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/octet-stream
< transfer-encoding: chunked
< date: Wed, 04 Mar 2020 15:03:39 GMT
<
* Connection #0 to host 127.0.0.1 left intact
helloworld

I think I understand the problem: add_underscore only lives as long as the hello_world function but the request lives longer because it's a future. I could make it 'static but in the real project that's something dynamic, created on demand (inside the function or injected as a parameter).

I think I have to attach the lifetime of add_underscore to the lifetime of the future (I think), but I don't know how.

I'm not even sure which part of the crates is the one requiring a 'static lifetime.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
MaC
  • 529
  • 7
  • 14
  • `.map(move |x| format!("{}{}", x, add_underscore))` – Shepmaster Mar 04 '20 at 15:19
  • It looks like your question might be answered by the answers of [Rust : borrowed value does not live long enough, moved due to use in closure E0597](https://stackoverflow.com/q/59506750/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Mar 04 '20 at 15:22
  • 1
    That was it! Thanks I cannot believe it. I have a mixed feeling between embarrassment and confusion with such an easy solution. Somehow I thought that the "never ending network request" was a huge problem for lifetimes here. I clearly didn't understood well the mechanics of `move` (I thought it was to move the ref in the closure). Thanks again – MaC Mar 05 '20 at 10:44

0 Answers0