1

I want to start a Hyper server in a function with port and dao parameters provided by main(), but the function only works after I explicitly indicate the 'static lifetime. This confused me a lot.

extern crate futures;
extern crate hyper;

use futures::future::Future;
use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use std::net::SocketAddr;

trait Dao {}

struct MysqlDao;

impl Dao for MysqlDao {}

struct HelloWorld<'a> {
    dao: &'a Dao,
}

const PHRASE: &'static str = "Hello, World!";

impl<'a> Service for HelloWorld<'a> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, _req: Request) -> Self::Future {
        Box::new(futures::future::ok(
            Response::new()
                .with_header(ContentLength(PHRASE.len() as u64))
                .with_body(PHRASE),
        ))
    }
}

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    let dao = MysqlDao;
    let server = Http::new()
        .bind(&addr, move || Ok(HelloWorld { dao: &dao }))
        .unwrap();
    server.run().unwrap();
}

The Http::new().bind API documententation said it needs a NewService + 'static, so I think the compiler would infer the dao variant is 'static, but when I move the last three statements out of main, it can't infer!

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    let dao: MysqlDao = MysqlDao;
    web_startup(&addr, &dao);
}

fn web_startup<T: Dao>(addr: &SocketAddr, dao: &T) {
    let server = Http::new()
        .bind(addr, move || Ok(HelloWorld { dao }))
        .unwrap();
    server.run().unwrap();
}

I get the error:

error[E0477]: the type `[closure@src/main.rs:44:21: 44:51 dao:&T]` does not fulfill the required lifetime
  --> src/main.rs:44:10
   |
44 |         .bind(addr, move || Ok(HelloWorld { dao }))
   |          ^^^^
   |
   = note: type must satisfy the static lifetime

So I fixed it:

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    static DAO: MysqlDao = MysqlDao;
    web_startup(&addr, &DAO);
}

fn web_startup<T: Dao>(addr: &SocketAddr, dao: &'static T) {
    let server = Http::new()
        .bind(addr, move || Ok(HelloWorld { dao }))
        .unwrap();
    server.run().unwrap();
}

I don't understand why I should use the static keyword for static DAO: MysqlDao = MysqlDao; statement but need not before change the code. The compiler couldn't infer it or am I thinking about things incorrectly?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
llxxbb
  • 1,241
  • 1
  • 10
  • 16
  • What *exactly* is the question you want to ask? It seems like you've solved your problem. Note that you should be able to put your question as the title of your post — that's a good sign that your question is focused enough to be worth answering. – Shepmaster Feb 18 '18 at 04:03
  • @Shepmaster The question is the last line "The compiler couldn't infer it or am I thinking about things incorrectly?". I think I should not explicit to indicate the static key word if the Compile be smart enough when I start server out of main(). BTW, I can't think out a simple title to match what i expressed. – llxxbb Feb 18 '18 at 04:15
  • Then it's probably a duplicate of [Why are explicit lifetimes needed in Rust?](https://stackoverflow.com/q/31609137/155423). – Shepmaster Feb 18 '18 at 15:38
  • I know what [Lifetime Elision](https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html#lifetime-elision) said, so I add lifetime to function `web_startup`. But it also need an explicit static `MysqlDao`. this is what I confused. @Shepmaster thanks for the title changed, but I think it deviate from the meaning. – llxxbb Feb 19 '18 at 01:35
  • Please feel free to change the title back if it's not accurate. Sorry that I don't understand what you are asking. – Shepmaster Feb 19 '18 at 17:07

1 Answers1

1

The reason the compiler cannot infer that the only time the web_startup function will be called it's called with a 'static is because that's not guaranteed. What if the function were public and it was called by a third-party module? The compiler would have to tell the end-user to use a 'static on a function that doesn't seem to require one. What if some time in the future eval() is added to Rust (e.g. for a REPL), so that even your private function could be called with unexpected function parameters?

You're asking for an inference that should not happen.

Fredrick Brennan
  • 7,079
  • 2
  • 30
  • 61
  • Thanks [Fredrick](https://stackoverflow.com/users/1901658/fredrick-brennan). I think `MysqlDao` instance should be used only once (by `web_startup`) in `main()`, there is no chance to be misused. If the compiler see the `web_startup` method need a `'static` reference, I think it can guarantee the `MysqlDao` instance to be static. – llxxbb Feb 20 '18 at 08:32
  • @llxxbb No. Being `static` is not the same as not being `mut`. A `static mut` is possible. Being `static` only means it won't be dropped for the duration of the program execution. It's a guarantee that it won't be dropped, because it isn't dropped until the end of main, which represents the end of program execution. That's why your program knows that `dao` is static. – Fredrick Brennan Feb 20 '18 at 12:57