Skip to content

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

New issue

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

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

Already on GitHub? # to your account

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
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants