Determining if u64 value fits in usize


#1

Hi,

I am (basically) trying to determine if a u64 value fits in a usize. In C this is straightforward:

bool fits_in_size_t(uint64_t v)
{
        return v <= SIZE_MAX;
}

Whereas in Rust, an explicit cast is required:

pub fn fits_in_usize(v: u64) -> bool {
    return v <= usize::max_value() as u64;
}

I am wondering:

  1. Is there a preferred way to avoid the cast, seeing as a cast might also indicate a (potentially unsafe) narrowing conversion as opposed to a (safe) widening conversion?
  2. Could there theoretically be a Rust implementation where a usize is larger than 64 bits? If so, my Rust code would sometimes be incorrect, whereas my C code would always be correct. I could not find a mention of restrictions on usize in Rust’s documentation.

#2

You can detect truncation without a constant: x as usize as u64 == x.


#3

Hmm… suppose that I instead wanted to know if twice the value of the u64 fits in a usize.

Again, in C this is straightforward:

bool fits(uint64_t v)
{
    return v <= SIZE_MAX / 2;
}

In Rust, it is this?

pub fn fits(v : u64) -> bool {
    v as usize as u64 == v && v as usize <= usize::max_value() / 2
}

#4

Is there a preferred way to avoid the cast, seeing as a cast might also indicate a (potentially unsafe) narrowing conversion as opposed to a (safe) widening conversion?

You could use num::ToPrimitive::to_usize, which returns None if the value does not fit in a usize. (The added checks will be optimized away when compiling for platforms where they are unnecessary.)

pub fn fits(v: u64) -> bool {
    match v.to_usize() {
        Some(v) => v < usize::max_value() / 2,
        None => false
    }
}


Could there theoretically be a Rust implementation where a usize is larger than 64 bits?

Yes. usize is always the same size as a pointer. Currently Rust only supports 32- and 64-bit platforms, but there’s no guarantee this will always be true. Theoretically Rust could be ported to some future platform with 128-bit pointers.


#5

To check if twice the value fits, x.checked_mul(2).is_some() (or match on it).


#6

And to find out if it fits into usize, you’d do

x.checked_mul(2).map(|v| v <= usize::max_value() as u64).unwrap_or(false)

?


#7

Never compare with a constant, that’s caused so many issues in C code (some of them exploitable).

You have to first check that it fits in usize verbatim (using a cast) and then check that the double also fits, e.g.:

pub fn fits(v: u64) -> bool {
    let u = v as usize;
    u as u64 == v && u.checked_mut(2).is_some()
}

#8

thanks… I fell into the same trap in the typestrong const int PR

Fixing during rebase