How to save app startup time to a constant var from SystemTime::now?

I'm trying to learn Rust, I've decided to start working on a simple API using Actix.

What I did was create a root endpoint that would return some basic info about the app (name, version, uptime).

Trying to generate current uptime is giving me a headache. At first I tried to create a constant, but I found out I cannot call non-const fn SystemTime::now in constants calls. After doing some research I found out I should be using Lazy to solve this problem.

This fixed the compile issue, but the problem now is that the uptime value is always 0. I'm guessing the reason for this is that Lazy always executes this method when called, which I do not want. How do I solve this?

This is my current implementation, essentially I want STARTUP_TIME to be initialized on app startup:

use actix_web::{get, HttpResponse, Responder};
use once_cell::sync::Lazy;
use serde::{Serialize};

#[derive(Serialize)]
pub struct Hello {
    pub name: String,
    pub uptime: u64,
    pub version: String,
}

const STARTUP_TIME: Lazy<std::time::SystemTime> = Lazy::new(|| std::time::SystemTime::now());

#[get("/")]
async fn hello() -> impl Responder {
  let startup_time = *STARTUP_TIME;
  let uptime = std::time::SystemTime::now()
      .duration_since(startup_time)
      .unwrap()
      .as_secs();
  let hello = Hello {
      name: env!("CARGO_PKG_NAME").to_string(),
      uptime,
      version: env!("CARGO_PKG_VERSION").to_string(),
  };
  HttpResponse::Ok().json(hello)
}

Your issue that you're misusing const. You actually want static here.

I think there's a proposed lint for const with internal mutable state.

2 Likes

If you think about it, putting the startup time into a const doesn't make sense. A const is evaluated at compile-time, thus if you managed to do this, you would get the timestamp of the compilation. (There's another good reason why SystemTime::now() isn't const in the first place: it calls out to the OS, so evaluating it at compile time is at best problematic, at worst impossible.)

Putting the thing in a static STARTUP_TIME: Lazy<SystemTime> as suggested above will only get you part of the way. There's still the problem that a Lazy is, well, lazy… therefore, it will not evaluate until the first time you query it. Thus, you should force it to evaluate the initializer upon startup explicitly, e.g. by accessing it in main() before you do anything else.

2 Likes

I see, that makes sense, I guess that's the reason why using const always resulted it to being set to 0 was that I'm using hot reload.

Yes, I've noticed Lazy only initialized on first invocation, I'll update my code to move the var to a new file and force initialize the STARTUP_TIME in main, thanks @erelde and @H2CO3!