23

I've got a struct defined that has a function which defines a static lifetime:

impl MyStruct {
    pub fn doSomething(&'static self) {
        // Some code goes here
    }
}

I'm consuming it from main like so:

fn main() {
    let obj = MyStruct {};
    obj.doSomething();
}

It's intended for the doSomething call to block and execute for the lifetime of the application.

I'm running into issues with the lifetime checks where it's stating that it may outlive the main function, which seems strange to me as once main is complete the application should exit.

Is there a way to achieve this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    What do you want to do exactly? – Boiethios Jun 07 '18 at 12:19
  • 1
    @Boiethios exactly what I stated, that function calls another function which requires a static lifetime, so the function itself requires a static lifetime in order to conform to those requirements. I need to call that function from main, which the compiler prevents due to the struct apparently outliving main. –  Jun 07 '18 at 12:30
  • What is this function that requires a static lifetime? – Boiethios Jun 07 '18 at 12:31
  • Your problem is that you require the reference to self to have a static livetime but it is defined in the main function. Try: https://play.rust-lang.org/?gist=0641e22a79898b268dec91e05ac1d160&version=stable&mode=debug – raggy Jun 07 '18 at 13:01
  • @Boiethios hyper requires a static runtime when running the server and processing each request. What's worse is it's actually two layers of static requirements (two closures). –  Jun 07 '18 at 13:09
  • @raggy unfortunately I have tried that, but it doesn't support dynamically configuring the struct, you have to have a plain struct value. –  Jun 07 '18 at 13:10
  • @lyptt Maybe you can use a lazy static – Boiethios Jun 07 '18 at 13:13
  • @Boiethios That solved it! Can you submit that as an answer so I can accept it? –  Jun 07 '18 at 13:20
  • @lyptt Sorry, I was busy. You can accept one of the answers that suit you – Boiethios Jun 07 '18 at 14:28

2 Answers2

21

The primary way to create a reference that has the 'static lifetime is to make the variable static. A static variable is one that can be created at compile time:

struct MyStruct;

impl MyStruct {
    pub fn do_something(&'static self) {}
}

static OBJ: MyStruct = MyStruct;

fn main() {
    OBJ.do_something();
}

As Rust's constant evaluation story improves, this will be more common, but it will never allow configuration at runtime.

A far less common method is to deliberately leak memory, producing a reference that will last "forever". This should be discouraged because leaking memory isn't a good thing:

fn main() {
    let obj = Box::leak(Box::new(MyStruct));
    obj.do_something();
}

There's also the possibility of creating a singleton:

as once main is complete the application should exit.

Perhaps, but the compiler doesn't treat main specially for lifetime purposes.


hyper requires a static runtime when running the server and processing each request.

No, it doesn't. It has a bound of : 'static, which means that any references passed in must be 'static, but you don't have to pass in a bare reference at all.

For patterns like this, the most common thing is to pass in something like an Arc. This allows sharing of the underlying resource.

pub fn do_something<F, T>(f: F)
where
    F: Fn() -> T + 'static,
    T: 'static,
{
    // "spawn" 3 threads
    f();
    f();
    f();
}

struct MyStruct;

static OBJ: MyStruct = MyStruct;

fn main() {
    // OK
    do_something(|| &OBJ);

    // Not OK
    let another = MyStruct;
    do_something(|| &another);

    // OK
    use std::sync::Arc;
    let shared = Arc::new(MyStruct);
    do_something(move || shared.clone());
}

You can even use interior mutability if you wanted dynamic reconfiguration.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Yeah I've got a wrapper class that needs a reference to `self`, I can pass in an Arc which works around that particular issue but it still has various `'static` complaints. –  Jun 07 '18 at 15:29
  • @Shepmaster from the rust reference "Static items do not call drop at the end of the program" so static and Box::leak are basically identical, except the latter are on the heap. Honestly given how annoying rust is about initializing static variables, Box::leak seems like a superior way to create static variables with runtime initialization. – Eloff Sep 23 '20 at 14:19
  • @Eloff I don't understand what the goal of your comment is. The answer already lists leaking memory as a solution. – Shepmaster Sep 23 '20 at 14:59
  • 1
    @Shepmaster You mentioned "This should be discouraged because leaking memory isn't a good thing", and indeed it sounds scary. But that's exactly how rust static works anyway, so I don't think it should be discouraged for this specific use case. – Eloff Sep 23 '20 at 15:36
13

The naive way to do this is with a static variable, but it will require unsafe code if you need to actually set the value inside your main function:

static mut OBJ: MyStruct = MyStruct;

fn main() {
    unsafe {
        OBJ = MyStruct {};
        OBJ.doSomething();
    }
}

It's also unsafe to do pretty much anything with a mutable static thereafter.

The much better way to do it is to let a library (lazy_static) take care of the unsafe code.

use lazy_static::lazy_static;

fn main() {
    lazy_static!{
        static ref OBJ: MyStruct = MyStruct {};
    }
    OBJ.doSomething();
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204