Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider reverting stabilization of Duration::from_nanos #51107

Closed
retep998 opened this issue May 27, 2018 · 14 comments
Closed

Consider reverting stabilization of Duration::from_nanos #51107

retep998 opened this issue May 27, 2018 · 14 comments
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@retep998
Copy link
Member

rust/src/libcore/time.rs

Lines 169 to 176 in 3fd82a5

#[stable(feature = "duration_extras", since = "1.27.0")]
#[inline]
pub const fn from_nanos(nanos: u64) -> Duration {
Duration {
secs: nanos / (NANOS_PER_SEC as u64),
nanos: (nanos % (NANOS_PER_SEC as u64)) as u32,
}
}

As it turns out, using u64 for Duration::from_nanos isn't the greatest idea because 2^64 / 1,000,000,000 / 60 / 60 / 24 / 365 comes out to 585 years which is rather short. I don't think the libs team realized how short this was when stabilizing it, but fortunately it hasn't actually hit stable yet and is still in beta so we have the time to reconsider that decision.

cc @SimonSapin who kicked off stabilization in #46507

@retep998 retep998 added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 27, 2018
@ishitatsuyuki
Copy link
Contributor

It's not clear if we may actually hit that limit. Filesystem like ext4, btrfs has nano second level timestamps but they are both capped at 64 bits.

One possible use case though, may be scientific emulation where this can potentially be a problem; but I don't actually do such emulations so I have no further ideas.

@SimonSapin SimonSapin added the beta-nominated Nominated for backporting to the compiler in the beta channel. label May 27, 2018
@SimonSapin
Copy link
Contributor

@retep998, what do you think of keeping it stable but changing the argument to u128 in beta?

CC @rust-lang/libs

@dtolnay
Copy link
Member

dtolnay commented May 27, 2018

I would prefer to keep it as u64. We provide other constructors for dealing with large durations, such as Duration::new. Ordinary overflow checks should catch when somebody multiplies numbers for a from_nanos argument and it overflows.

@retep998
Copy link
Member Author

It's not clear if we may actually hit that limit. Filesystem like ext4, btrfs has nano second level timestamps but they are both capped at 64 bits.

Meanwhile on Windows NTFS has 64bit timestamps that are multiples of 100ns, which can overflow a u64 from_nanos.

@hanna-kruppe
Copy link
Contributor

@SimonSapin Changing the argument to u128 means the conversion can fail, since Duration stores seconds in an u64 (and this is effectively exposed in stable, infallible APIs like as_secs) and 2^128 is much larger than 2^64 * NANOS_PER_SEC. So it would not be a simple search-and-replace job but require some redesign as well (if it's feasible at all, we may not want the convenience constructor to be fallible).

@alexcrichton
Copy link
Member

I agree with @dtolnay in that we've got other constructors for dealing with overflow or larger durations, so having this as a convenience constructor seems like it should be good to do still.

@sfackler
Copy link
Member

I also prefer a u64 argument.

@SimonSapin
Copy link
Contributor

I find it convincing that converting an NTFS timestamp to a Duration is better served by a few lines of arithmetic and Duration::new. How does that sound, @retep998?

@retep998
Copy link
Member Author

retep998 commented Jun 1, 2018

Another point. If we ever add methods like as_nanos that return a u128, the user wouldn't be able to immediately pass that value back to from_nanos.

@pietroalbini
Copy link
Member

The consensus from the libs team seems to be to keep using u64, right? 1.27.0 is approaching.

@sfackler
Copy link
Member

sfackler commented Jun 2, 2018

@pietroalbini I believe so, yes.

@pietroalbini pietroalbini removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jun 2, 2018
@pietroalbini
Copy link
Member

Well, this can be closed then.

@Ekleog
Copy link

Ekleog commented Mar 23, 2020

I guess it's too late, but IMO a compelling argument for that change would have been that Duration::from_nanos(d.as_nanos()) does not compile.

That said, now that it's stable, I guess changing it even in a new edition would basically not be doable?

@ChocolateLoverRaj
Copy link

Like @retep998 said, there is now the inconsistency that as_nanos outputs u128 but from_nanos needs u64. This is inconsistent.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 11, 2024
…shtriplett

Suggest less bug-prone construction of Duration in docs

std::time::Duration has a well-known quirk: Duration::as_nanos() returns u128 [1], but Duration::from_nanos() takes u64 [2]. So these methods cannot easily roundtrip [3]. It is not possible to simply accept u128 in from_nanos [4], because it requires breaking other API [5].

It seems to me that callers have basically only two options:
1. `Duration::from_nanos(d.as_nanos() as u64)`, which is the "obvious" and buggy approach.
2. `Duration::new(d.as_secs(), d.subsecs_nanos())`, which only becomes apparent after reading and digesting the entire Duration struct documentation.

I suggest that the documentation of `from_nanos` is changed to make option 2 more easily discoverable.

There are two major usecases for this:
- "Weird math" operations that should not be supported directly by `Duration`, like squaring.
- "Disconnected roundtrips", where the u128 value is passed through various other stack frames, and perhaps reconstructed into a Duration on a different machine.

In both cases, it seems like a good idea to not tempt people into thinking "Eh, u64 is good enough, what could possibly go wrong!". That's why I want to add a note that points out the similarly-easy and *safe* way to reconstruct a Duration.

[1] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_nanos
[2] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos
[3] https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fa6bab2b6b72f20c14b5243610ea1dde
[4] rust-lang#103332
[5] rust-lang#51107 (comment)
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Feb 11, 2024
Rollup merge of rust-lang#119242 - BenWiederhake:dev-from-nanos, r=joshtriplett

Suggest less bug-prone construction of Duration in docs

std::time::Duration has a well-known quirk: Duration::as_nanos() returns u128 [1], but Duration::from_nanos() takes u64 [2]. So these methods cannot easily roundtrip [3]. It is not possible to simply accept u128 in from_nanos [4], because it requires breaking other API [5].

It seems to me that callers have basically only two options:
1. `Duration::from_nanos(d.as_nanos() as u64)`, which is the "obvious" and buggy approach.
2. `Duration::new(d.as_secs(), d.subsecs_nanos())`, which only becomes apparent after reading and digesting the entire Duration struct documentation.

I suggest that the documentation of `from_nanos` is changed to make option 2 more easily discoverable.

There are two major usecases for this:
- "Weird math" operations that should not be supported directly by `Duration`, like squaring.
- "Disconnected roundtrips", where the u128 value is passed through various other stack frames, and perhaps reconstructed into a Duration on a different machine.

In both cases, it seems like a good idea to not tempt people into thinking "Eh, u64 is good enough, what could possibly go wrong!". That's why I want to add a note that points out the similarly-easy and *safe* way to reconstruct a Duration.

[1] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_nanos
[2] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos
[3] https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fa6bab2b6b72f20c14b5243610ea1dde
[4] rust-lang#103332
[5] rust-lang#51107 (comment)
lnicola pushed a commit to lnicola/rust-analyzer that referenced this issue Apr 7, 2024
Suggest less bug-prone construction of Duration in docs

std::time::Duration has a well-known quirk: Duration::as_nanos() returns u128 [1], but Duration::from_nanos() takes u64 [2]. So these methods cannot easily roundtrip [3]. It is not possible to simply accept u128 in from_nanos [4], because it requires breaking other API [5].

It seems to me that callers have basically only two options:
1. `Duration::from_nanos(d.as_nanos() as u64)`, which is the "obvious" and buggy approach.
2. `Duration::new(d.as_secs(), d.subsecs_nanos())`, which only becomes apparent after reading and digesting the entire Duration struct documentation.

I suggest that the documentation of `from_nanos` is changed to make option 2 more easily discoverable.

There are two major usecases for this:
- "Weird math" operations that should not be supported directly by `Duration`, like squaring.
- "Disconnected roundtrips", where the u128 value is passed through various other stack frames, and perhaps reconstructed into a Duration on a different machine.

In both cases, it seems like a good idea to not tempt people into thinking "Eh, u64 is good enough, what could possibly go wrong!". That's why I want to add a note that points out the similarly-easy and *safe* way to reconstruct a Duration.

[1] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_nanos
[2] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos
[3] https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fa6bab2b6b72f20c14b5243610ea1dde
[4] rust-lang/rust#103332
[5] rust-lang/rust#51107 (comment)
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this issue Apr 27, 2024
Suggest less bug-prone construction of Duration in docs

std::time::Duration has a well-known quirk: Duration::as_nanos() returns u128 [1], but Duration::from_nanos() takes u64 [2]. So these methods cannot easily roundtrip [3]. It is not possible to simply accept u128 in from_nanos [4], because it requires breaking other API [5].

It seems to me that callers have basically only two options:
1. `Duration::from_nanos(d.as_nanos() as u64)`, which is the "obvious" and buggy approach.
2. `Duration::new(d.as_secs(), d.subsecs_nanos())`, which only becomes apparent after reading and digesting the entire Duration struct documentation.

I suggest that the documentation of `from_nanos` is changed to make option 2 more easily discoverable.

There are two major usecases for this:
- "Weird math" operations that should not be supported directly by `Duration`, like squaring.
- "Disconnected roundtrips", where the u128 value is passed through various other stack frames, and perhaps reconstructed into a Duration on a different machine.

In both cases, it seems like a good idea to not tempt people into thinking "Eh, u64 is good enough, what could possibly go wrong!". That's why I want to add a note that points out the similarly-easy and *safe* way to reconstruct a Duration.

[1] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.as_nanos
[2] https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos
[3] https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fa6bab2b6b72f20c14b5243610ea1dde
[4] rust-lang/rust#103332
[5] rust-lang/rust#51107 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants