std time
— 2019-06-25

Over the past month we've been hard at work to add time support to the Runtime crate. One of the things we've had to think about has been examples. Which means we've had a chance to become intimately familiar with the good and less good parts of the std::time API.

In this post we'll look at the std::time API, and some of the proposed changes to smooth things out a bit. Also disclaimer: I've been involved with these proposals, hehe.

Time Types

A quick refresher on what's inside std::time. There are two types you need to know about: Duration and Instant. An Instant represents a specific point in time. A Duration is a relative time offset. This is different from some languages which only have a single time type. Personally I quite like Rust's approach of having two.

Let's put the time types together to first get the current timestamp. Then sleep the thread for 2 seconds. And finally print how much time has passed.

use std::time::{Duration, Instant};
use std::thread::sleep;

let now = Instant::now();
sleep(Duration::from_secs(2));
println!("{}", now.elapsed().as_secs());

Creating Durations

As we've seen before, durations can be created using Duration::from_secs(n). This works well, but is rather verbose. Not only do we have to import two layers deep, we also need to remember the name of the method. All in all it takes some getting used to.

Which is why in rust-lang/rust#57391 there's a proposal to add constants to the time module. These constants would represent a single unit of time, which could then be used for addition, substraction and multiplication. E.g. 5 milliseconds is 5 times the millisecond constant.

The exact API is still undecided. But I think it would be nice if the APIs were brief, and exposed directly under std::time. This would allow us to rewrite our example as:

use std::time::Instant;
use std::thread::sleep;

let now = Instant::now();
sleep(2 * time::SEC);
println!("{}", now.elapsed().as_secs());

Creating Instants

Just like creating Durations can likely be simplified, so can creating Instants. The most common way of creating an Instant is by calling Instant::now. This too has the problem that we need to remember the exact type, and call two layers deep into the std hierarchy.

Which is why rust-lang/rust#62114 proposes to add a time::now method. This behaves the same as time::Instant::now, but should be more pleasant to use. An example:

use std::{time, thread};

let now = time::now();
thread::sleep(2 * time::SEC);
println!("{}", now.elapsed().as_secs());

Debug Durations

One of the first gotchas you might experience when dealing with time in Rust is that you cannot simply print a Duration. If you're like me, you'll run into this, and after some searching find out that you need to specify a resolution before you can print. E.g. dur.as_secs or dur.as_millis.

This is not great because if you're doing exploratory programming and/or debugging, you may not know the resolution up front. Which means you might need a few tries to find the right resolution. Which isn't a dealbreaker, but it takes speed out of the process.

Instead it would be nice if we could print Durations (and Instants for that matter), where they would provide data for all resolutions it encapsulates.

Duration {
    secs: 6,
    millis: 6225,
    micros: 6224768,
    nanos: 6224767054,
}

Perhaps we might also need a pretty-printed version of this. E.g. print 6.225 secs as the Display impl. I'm not sure whether that's acceptable for a stdlib extension, but I sure know that it would make debugging time a lot easier.

Anyway, let's apply this to our example:

use std::{time, thread};

let now = time::now();
thread::sleep(2 * time::SEC);
dbg!(now.elapsed());

Conclusion

In this post we've talked about the std::time module, and some of the changes that could be made to smooth out the workflow.

I hope this was a useful insight into how the std::time module can be improved. I figured I'd write this post because I spent some time earlier today filing an issue, and thought sharing an overarching vision might be nice.

Thanks for reading all, and hope you have a lovely (non-scorched) week!