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

Duration.as_nanos() as u64 exceeds u64::MAX #137814

Closed
marcusdesai opened this issue Feb 28, 2025 · 7 comments
Closed

Duration.as_nanos() as u64 exceeds u64::MAX #137814

marcusdesai opened this issue Feb 28, 2025 · 7 comments

Comments

@marcusdesai
Copy link

I tried this code:

use std::time::Duration;

fn main() {
    let duration = Duration::from_secs(u64::MAX);
    assert_eq!(duration.as_nanos() as u64, u64::MAX)
}

I expected to see this happen: The assert to pass.

Instead, this happened: The assert fails with the following:

assertion `left == right` failed
  left: 18446744072709551616
 right: 18446744073709551615

Does not seem related to u128, as this passes:

assert_eq!(u128::MAX as u64, u64::MAX)

Meta

rustc --version --verbose:

rustc 1.84.0 (9fc6b4312 2025-01-07)
binary: rustc
commit-hash: 9fc6b43126469e3858e2fe86cafb4f0fd5068869
commit-date: 2025-01-07
host: x86_64-apple-darwin
release: 1.84.0
LLVM version: 19.1.5
Backtrace

I doub't the backtrace is much use, but just in case.

   0: rust_begin_unwind
             at /rustc/9fc6b43126469e3858e2fe86cafb4f0fd5068869/library/std/src/panicking.rs:665:5
   1: core::panicking::panic_fmt
             at /rustc/9fc6b43126469e3858e2fe86cafb4f0fd5068869/library/core/src/panicking.rs:76:14
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
   4: ansa::wait::tests::test
             at ./src/wait.rs:620:9
   5: ansa::wait::tests::test::{{closure}}
             at ./src/wait.rs:618:14
   6: core::ops::function::FnOnce::call_once
             at /Users/blank/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/9fc6b43126469e3858e2fe86cafb4f0fd5068869/library/core/src/ops/function.rs:250:5

@marcusdesai marcusdesai added the C-bug Category: This is a bug. label Feb 28, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Feb 28, 2025
@marcusdesai
Copy link
Author

Fails also on both:

rustc 1.85.0 (4d91de4e4 2025-02-17)
binary: rustc
commit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688
commit-date: 2025-02-17
host: x86_64-apple-darwin
release: 1.85.0
LLVM version: 19.1.7

and,

rustc 1.87.0-nightly (96cfc7558 2025-02-27)
binary: rustc
commit-hash: 96cfc75584359ae7ad11cc45968059f29e7b44b7
commit-date: 2025-02-27
host: x86_64-apple-darwin
release: 1.87.0-nightly
LLVM version: 20.1.0

@ChrisDenton
Copy link
Member

This seems expected to me. The code essentially minimizes to:

18446744073709551615000000000_u128 as u64

That is u64::MAX seconds converted to 128-bit nano seconds and then truncated to 64-bits using the lossy as cast.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Feb 28, 2025

Spelling out the answer to the issue title because this confused me too:

assertion `left == right` failed
  left: 18446744072709551616
 right: 18446744073709551615       [ = u64::MAX ]

these numbers happen to look extremely similar in decimal and the first one ends in ...1616 while u64::MAX ends in ...1615, but first number is actually smaller: 18446744072... < 18446744073...

Indeed this modified assertion passes (and is flagged by clippy as being tautological):

use std::time::Duration;

fn main() {
    let duration = Duration::from_secs(u64::MAX);
    assert!(duration.as_nanos() as u64 <= u64::MAX);
}

@ChrisDenton
Copy link
Member

What's happening is somewhat clearer if you print the hex and align:

0x3b9ac9ffffffffffc4653600    nanos as u128
        0xffffffffc4653600    nanos as u64
        0xffffffffffffffff    u64::MAX

@marcusdesai
Copy link
Author

marcusdesai commented Feb 28, 2025

Thanks! This all makes sense and I had indeed missed that the as u64 cast resulted in a smaller number than u64::MAX. Nonetheless, it feels very unituitive that u64::MAX of seconds truncated to u64 nanoseconds could be less than u64::MAX. I would expect to end up with as many nanoseconds as possible, in a u64.

Is that just because of the as cast?

@hanna-kruppe
Copy link
Contributor

Yes, casting from a larger integer to a smaller one with as just takes the least significant bits, so e.g. 0x100u32 as u8 == 0. If you want different behavior for values that don’t fit in the destination type, use try_into() and handle the error case.

@marcusdesai
Copy link
Author

marcusdesai commented Feb 28, 2025

Thank you, I've definitely had this misconception about how as works for a while. Btw, for anyone else finding this I also just came across https://internals.rust-lang.org/t/lets-deprecate-as-for-lossy-numeric-casts/16283 when searching about this.

EDIT: and this rfc: rust-lang/rfcs#2484 (comment)

@jieyouxu jieyouxu removed C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Mar 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants