Duration::as_nanos() hands you a u128. Duration::from_nanos() takes a u64. You feed one into the other and the compiler yells at you โ or worse, you cast and quietly truncate at 584 years. Rust 1.93 closed the loop with from_nanos_u128.
The mismatched-types papercut
The old API was asymmetric. Going from Duration to nanos was 128-bit:
1
2
3
4
5
| use std::time::Duration;
let d = Duration::new(7, 250);
let n: u128 = d.as_nanos();
assert_eq!(n, 7_000_000_250);
|
Coming back, though, you only got from_nanos(_: u64) โ so the round-trip needed a cast:
1
2
3
4
5
| use std::time::Duration;
let n: u128 = Duration::new(7, 250).as_nanos();
let back = Duration::from_nanos(n as u64); // narrowing cast, fingers crossed
assert_eq!(back, Duration::new(7, 250));
|
That as u64 silently truncates anything past u64::MAX โ and u64::MAX nanoseconds is roughly 584 years. Inside a calendar app you’ll never notice. Inside a scientific or simulation context, you absolutely will.
from_nanos_u128 matches as_nanos
Rust 1.93 stabilised Duration::from_nanos_u128, a const fn that takes the full 128-bit value:
1
2
3
4
5
| use std::time::Duration;
let n: u128 = Duration::new(7, 250).as_nanos();
let back = Duration::from_nanos_u128(n);
assert_eq!(back, Duration::new(7, 250));
|
Same shape on both sides. No cast, no truncation, no silent wraparound.
Past the 584-year ceiling
Where the new constructor actually earns its keep is when you have nanoseconds counts that wouldn’t fit in a u64:
1
2
3
4
5
6
7
8
9
| use std::time::Duration;
// 10^24 ns is ~31.7 million years โ well past u64::MAX nanos
let nanos: u128 = 10_u128.pow(24) + 321;
let d = Duration::from_nanos_u128(nanos);
assert_eq!(d.as_secs(), 10_u64.pow(15));
assert_eq!(d.subsec_nanos(), 321);
assert_eq!(d.as_nanos(), nanos); // exact round-trip
|
Duration itself stores (u64 seconds, u32 nanos), so it has plenty of room โ the old from_nanos was just bottlenecked by its argument type.
One thing to watch
from_nanos_u128 panics if you hand it more than Duration::MAX worth of nanoseconds. If you’re pulling values from user input or untrusted sources, guard the upper bound yourself โ there isn’t a checked_from_nanos_u128 (yet).
When to reach for it
Use from_nanos_u128 whenever you already have a u128 of nanoseconds โ typically because it came out of as_nanos, an arithmetic accumulator, or a high-precision external clock. Stick with the plain from_nanos(_: u64) for short-lived timeouts and durations measured in milliseconds or seconds; the u64 is plenty.
Stabilised in Rust 1.93 (January 2026). Available as const fn, so it works in const contexts too.