Why is std::time::Instant opaque?

I understand that. Adapting Rust to better suit domains it hasn't considered much is a language design problem, which is off-topic for this forum; airing concerns and figuring out how the language can change to address them is the purpose of IRLO, which is why I directed you there.

The closely-related issue of working around Rust's current limitations in unusual (for Rust) situations is on topic here, however, so that's the flavor of response you'll get. For the purposes of URLO discussions, changing Rust isn't a viable option— Lobbying and waiting for a language change is too slow of a process when you're trying to solve an actual, current, real-world problem.

It would also help if you clearly state that you're working in a specialized domain. Most of the responses have directly answered the root question you asked in the title, "Why is std::time::Instant opaque," with the reasons the current design was chosen. These answers have necessarily been couched in general terms because you've given no indication of the domain you're working in.

Even now, after reading a fairly long thread, I have no idea what "your field" is, so there's no possible way for me to anticipate which of many potential concerns will be important to you. By definition, you're the expert here in your field, so it's your job to educate us about any specialized requirements you have. Barring that, the advice you receive will necessarily feel like a bad fit for your situation.

4 Likes

I thought I was pretty clear, maybe not? Or maybe they were lost in the rest. It is most definitely not my job to educate you about projects I'm involved in. How about people assume the author generally knows what they are talking about and the thread flows along the questions asked instead of the constant attempts by people to second guess the author? Don't worry, It isn't unique to here at all. I generally start assuming the highest competency and then start lowering it as it become apparent the level of discourse. The assume the person is an idiot and raise your view of them if they agree with you seems all the rage, though.

The original question was "why is it opaque" and the only answer given is that it is different on different archs. That doesn't seem to have stopped other parts of std from exporting functions that just panic if you call on the wrong arch - that seems far worse since they will actually compile and blow up at runtime. If is just author whim to a large extent?

If that really is the reason wny though - are all differences hidden? I cant' think of any structs that are different really, but I dont know them well. Why arent the calls that panic removed for archs for not allowed entirely? That's seems a bigger oversight.

And it isn't really about the topic either. If had a love rest of how awesome the API was and was excited because of all these other changes I hope they would implement, this discussion wouldn't be happening. There could be a 100 message long saccharine topic here and the author wouldn't be told it wasn't wanted here. I could be wrong, but I'm almost positive that would be the case. So it isn't the topic, just that the topic isn't so as positive as people would wish.

My last comment was removed by the community even though it didn't break any rules just expressed exasperation. The silencing of someone by the larger community is absolutely against the CoC though.

Oh I'm sorry, I didn't know about that area and will try to segment the conversations appropriately. Thanks.

1 Like

QPC has units and a definition. Its epoch isn't guaranteed, but it has enough to turn into a useful value that is significant;y more than an Instant gives you. (if you really need the epoch you can determine it). Windows doesn't do time well, and you would hope the library writers wouldn't let it pull down everything else. Even if I just knew nanos from the QPC and linux version, since it documents where the value comes from, on linux i can get to a iso timestamp if i want. (win also, just more steps).

sorry, to be very concrete, I wanted to know why the type was opaque (there are other places where platform distinctions are allowed to leak through the api). I waned either to seethe type and let me use it (I can't crash anything with some read only numbers) or give a lossless to_nanos call to extract the value from it. This isn't a safety concern so I didn't see the problem.

What I was trying to make clear is that with an arbitrary epoch, an Instant can be converted to nanos just fine:

use once_cell::sync::Lazy;
use std::time::Instant;

static EPOCH: Lazy<Instant> = Lazy::new(Instant::now);

fn to_nanos(t: Instant) -> u128 {
    let epoch = *EPOCH;
    (t - epoch).as_nanos()
}

The nanos retrieved from CLOCK_MONOTONIC don't correspond to Unix-time nanos anyway, so I'm having trouble understanding what more you're asking for. (Keep in mind that a monotonic clock is fundamentally unable to use Unix-time nanos, since Unix time skips over leap seconds whereas monotonoic clocks do not.)

And to make some old hack come bite me in teh ass harder, I used the ns to order some timeout after others between REALTIME and MONO (bad hack, I know). I can get rid of that, but if I want my expiring timers to order in the same way and hopefully not error out because it thinks they are too early maybe, possible, potentially?

I could have made my solution clearer as well. What I'm proposing is a "fake SystemTime" that's really a shorthand for a duration after the epoch Instant. It can be converted to and from a real SystemTime, and that conversion will add/subtract a fixed, small realtime error, except in the event of leap seconds or system time adjustments. To expand on my earlier idea (Rust Playground):

use once_cell::sync::Lazy;
use std::time::{Duration, Instant, SystemTime};

static EPOCH: Lazy<(Instant, SystemTime)> = Lazy::new(|| (Instant::now(), SystemTime::now()));

#[derive(Copy, Clone)]
pub struct FakeSystemTime(Duration);

impl FakeSystemTime {
    pub fn now() -> FakeSystemTime {
        let (epoch, _) = *EPOCH;
        FakeSystemTime(Instant::now() - epoch)
    }

    pub fn from_instant(t: Instant) -> FakeSystemTime {
        let (epoch, _) = *EPOCH;
        FakeSystemTime(t - epoch)
    }

    pub fn from_system(t: SystemTime) -> FakeSystemTime {
        let (_, start) = *EPOCH;
        FakeSystemTime(t.duration_since(start).unwrap())
    }

    pub fn from_unix_nanos(nanos: u64) -> FakeSystemTime {
        let (_, start) = *EPOCH;
        let t = SystemTime::UNIX_EPOCH + Duration::from_nanos(nanos);
        FakeSystemTime(t.duration_since(start).unwrap())
    }

    pub fn as_instant(&self) -> Instant {
        let (epoch, _) = *EPOCH;
        epoch + self.0
    }

    pub fn as_system(&self) -> SystemTime {
        let (_, start) = *EPOCH;
        start + self.0
    }

    pub fn as_nanos_since_epoch(&self) -> u128 {
        self.0.as_nanos()
    }

    pub fn as_unix_nanos(&self) -> u128 {
        let (_, start) = *EPOCH;
        let duration = (start + self.0).duration_since(SystemTime::UNIX_EPOCH);
        duration.unwrap().as_nanos()
    }
}

Only comparing FakeSystemTime::now() against a real SystemTime::now() can reveal the discrepancy, so the latter should be replaced with the Instant-based FakeSystemTime::now().as_system().

1 Like

It seems like a lit of contortions for something that will be off a little (notr worried about absolute time being off but some ordering issues). I punted on REALTIME clock timers, and printing is a little wonky (they print out at a slightly off time stamp so it looks like they hvent expire yet but they have - this is just a debugging nuisance not an issue besides that.

You wre clear - ( understood that you wanted to make an impromptu epopch. My minii system I'm doing I'll prob just copy the c++ and keep unix nanos for everything since I'm not sure I'll include the REALTIME timers for the POC I'm doing.

thakns though, you really didn't need to do all that, but it is appreciated.