0

I want to parse a YAML file and use the value inside a service for a HTTP request. Line 35 is the end of main function.

extern crate hyper;
extern crate libc;
extern crate yaml_rust;

use hyper::rt::Future;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use std::sync::Arc;
use yaml_rust::YamlLoader;

fn main() {
    let content: String = String::from("response: Hello world");
    let cfg = Arc::new(YamlLoader::load_from_str(content.as_str()).unwrap());
    let cfg0 = (&cfg[0]).clone();
    let cfg_response = (&cfg0)["response"].as_str().unwrap();

    // A `Service` is needed for every connection, so this
    // creates on of our `hello_world` function.
    let handle = move || {
        let cfg_response = cfg_response.clone();

        service_fn_ok(move |_| Response::new(Body::from(String::from(cfg_response.clone()))))
    };

    // Serve HTTP protocol
    // This is our socket address...
    let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();

    let server = Server::bind(&addr)
        .serve(handle)
        .map_err(|e| eprintln!("server error: {}", e));

    // Run this server for... forever!
    hyper::rt::run(server);
}

Unfortunately, I encountered a nested closure leading to a strange borrow error:

error[E0597]: `cfg0` does not live long enough
  --> src/main.rs:15:26
   |
15 |     let cfg_response = (&cfg0)["response"].as_str().unwrap();
   |                          ^^^^ borrowed value does not live long enough
...
35 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

I tried to

  1. clone it before borrow
  2. use Arc to make it counter-based,
  3. modify assignment

all to no avail

Why does this happen? How do I solve this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Abdillah
  • 982
  • 11
  • 28
  • 1
    What is the point of your Arc ? `cfg_response` is a `&str` so it's must live as long as your closure live so... forever ? That why compiler ask you static storage. You could transform the reference to a string. `let cfg_response = (&cfg0)["response"].as_str().unwrap().to_string();` – Stargateur Sep 19 '18 at 05:29
  • Umm... no point ^^. I haven't fully grasp the `Rc` (my plan is instead of `move`, I'll try to counting-based borrow). I hope other parts is clear enough. Okay, I will try to transform it. – Abdillah Sep 19 '18 at 06:47

1 Answers1

0

The functions you pass the closure into - hyper::server::Builder::serve and hyper::rt::run() - require their arguments to be 'static, rather than being bounded by any function. main isn't considered special in this regard.

The value bounding it, cfg_response, is captured by the outer closure, so the nested closure isn't necessary to get the error. Here's a very small program that has the same problem:

fn main() {
    let cfg0 = String::from("hello world");
    let cfg_response: &str = &cfg0;

    let handle = move || {
        // this closure takes ownership of cfg_response, a reference to cfg0.  Since cfg0 will not
        // outlive the function, neither can handle.  If it instead took ownership of cfg0 or a
        // clone of it, it would have no outside references and could live forever.
        return cfg_response.to_owned();
    };

    serve(handle);
}

fn serve<F: Fn() -> String + 'static>(handle: F) {
    loop {
        println!("{}", handle());
    }
}

As @Stargateur pointed out, this can be solved by making cfg_response owned.

Alternatively, you could initialize cfg0 in a lazy_static like so:

#[macro_use]
extern crate lazy_static;

lazy_static! {
    static ref cfg0: String = String::from("hello world");
}

This way, you can still use a borrowed value because it meets the lifetime requirements.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
wartmanm
  • 326
  • 2
  • 7