Struct in struct with rouille: does not live long enough

I have a Client which holds a Config and it looks to me as if they have the same scope but I'm having an issue with lifetimes.

use rouille::Response;

struct Config {
    foo: String,
}

struct Client<'a> {
    config: &'a Config,
}
impl<'a> Client<'a> {
    fn new(config: &'a Config) -> Self {
        Client { config }
    }
    fn print_foo(&self) {
        println!("{}", self.config.foo);
    }
}

struct AnotherClient<'a> {
    config: &'a Config,
}
impl<'a> AnotherClient<'a> {
    fn new(config: &'a Config) -> Self {
        AnotherClient { config }
    }
    fn print_foo(&self) {
        println!("{}", self.config.foo);
    }
}

fn main() {
    let config = Config {foo: String::from("foo")};
    let client = Client::new(&config);
	let another_client = AnotherClient::new(&config);
	rouille::start_server("127.0.0.1:1234", move |req| {
		client.print_foo();
		another_client.print_foo();
		Response::text("")
	});
}
error[E0597]: `config` does not live long enough
  --> src\main.rs:33:30
   |
32 |     let config = Config {foo: String::from("foo")};
   |         ------ binding `config` declared here
33 |     let client = Client::new(&config);
   |                  ------------^^^^^^^-
   |                  |           |
   |                  |           borrowed value does not live long enough
   |                  argument requires that `config` is borrowed for `'static`
...
40 | }
   | - `config` dropped here while still borrowed

I tried to eliminate rouille from this minimal example but then the issue doesn't happen, so I'm guessing it has to do with the threaded nature of rouille.

Sure, I could clone the Config here, it's just a couple of strings, but I was wondering if there is a way to not duplicate the Config.

Make the client own Config, and don't use temporary scope-bound references in structs.

If the Config is large and you want to store it "by reference", then use Box<Config>. If one config instance must be reused many times and is too expensive to copy, then use Arc<Config>.

A temporary view & is just not flexible enough and there are plenty of cases where it's impossible to use it, at least not without causing memory leaks. In your case it won't be possible to send Client to another thread (I guess that's where 'static requirement comes from), because that would create a self-referential type.

You should avoid putting temporary references in structs.

Alright, I'll copy in this case and I'll use Arc in cases with larger or un-copy-able structs.

I don't understand how Box would help here, though.

The reason is a 'static bound on rouille::start_server's second argument:

pub fn start_server<A, F>(addr: A, handler: F) -> !
where
    A: ToSocketAddrs,
    F: Send + Sync + 'static + Fn(&Request) -> Response,

The handler of type F must be 'static (edit: which is likely due to threading as you suspected, I guess).

Here is an example triggering the error without using rouille:

fn start<F>(_handler: F) where
    F: Send + Sync + Fn(),
    F: 'static, // commenting out this makes `main` compile
{
}

fn main() {
    let config = Config {foo: String::from("foo")};
    let client = Client::new(&config);
	let another_client = AnotherClient::new(&config);
	start(move || {
		client.print_foo();
		another_client.print_foo();
	});
}

(Playground)


A possible solution is to use Arc:

use std::sync::Arc;

struct Config {
    foo: String,
}

struct Client {
    config: Arc<Config>,
}
impl Client {
    fn new(config: Arc<Config>) -> Self {
        Client { config }
    }
    fn print_foo(&self) {
        println!("{}", self.config.foo);
    }
}

(Playground)

I think @kornel means that you could solve the problem by removing the lifetime from Config Client and cloning your Config where needed. The Box would just keep the Client smaller in size (it's not solving your lifetime problem). This is how it could work with Box: Playground. Or without Box: Playground.

The Arc, in contrast, allows you avoiding cloning the Config.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.