Why is type conversion from u64 to usize allowed using `as` but not `From`?

15,810

Solution 1

as casts are fundamentally different from From conversions. From conversions are "simple and safe" whereas as casts are purely "safe". When considering numeric types, From conversions exist only when the output is guaranteed to be the same, i.e. there is no loss of information (no truncation or flooring or loss of precision). as casts, however, do not have this limitation.

Quoting the docs,

The size of [usize] is "how many bytes it takes to reference any location in memory. For example, on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes."

Since the size depends on the target architecture and cannot be determined before compilation, there is no guarantee that a From conversion between a numeric type and usize is possible. An as cast, however, will always operate by the rules listed here.

For instance, on a 32-bit system, usize is equivalent to u32. Since a usize is smaller than a u64, there can be loss of information (truncation) when converting a u64 into a usize and hence a From conversion cannot exist. However, the size of a usize is always guaranteed to be 8 bits or greater and a u8 to usize From conversion will always exist.

Solution 2

As already mentioned, converting from a 64-bit value to a usize might cause truncation; you might lose data when a usize is 16 or 32 bits.

Fallable conversions are covered by the TryFrom trait, available in Rust 1.34:

use std::convert::TryFrom;

fn main() {
    let a: u64 = 5;
    let b = a as usize;
    let b = usize::try_from(a);
}

See also:

Share:
15,810

Related videos on Youtube

Kağan Kayal
Author by

Kağan Kayal

I am a space engineer with a focus on operations and ground segment. I am increasingly applying web technologies for operations preparation and post processing. Mostly PHP, sqlite, HTML/CSS/Javascript. For processing binary data, I prefer C++ and Rust.

Updated on September 14, 2022

Comments

  • Kağan Kayal
    Kağan Kayal over 1 year

    The first conversion using 'as' compiles, but the second one using the 'From' trait does not:

    fn main() {
        let a: u64 = 5;
        let b = a as usize;
        let b = usize::from(a);
    }
    

    Using Rust 1.34.0, I get the following error:

    error[E0277]: the trait bound `usize: std::convert::From<u64>` is not satisfied
     --> src/main.rs:4:13
      |
    4 |     let b = usize::from(a);
      |             ^^^^^^^^^^^ the trait `std::convert::From<u64>` is not implemented for `usize`
      |
      = help: the following implementations were found:
                <usize as std::convert::From<bool>>
                <usize as std::convert::From<std::num::NonZeroUsize>>
                <usize as std::convert::From<u16>>
                <usize as std::convert::From<u8>>
      = note: required by `std::convert::From::from`
    

    When I replace u64 with u8, there is no more error. From the error message, I understand that the From trait is implemented only for u8, but not for the other integer types.

    If there is a good reason for that, then why shouldn't the conversion using 'as' should also fail to compile?

  • Kağan Kayal
    Kağan Kayal over 6 years
    With this understanding, I tried println!("{}", 500u16 as u8); and get 244. I assume "safe" means here just the fact that we cannot mess with the memory, but we still might end up with a completly wrong number. Since I hesitate to call this "safe" from the math point of view, I suggest to replace "casts are purely "safe"" with "casts are only "memory safe", but might change the value" or so in your answer. This way, a future reader would have a stronger warning about potential bugs they can still create and would avoid a false sense of “safety”.
  • Stefan
    Stefan over 6 years
    rust reference: Unsafety: "Unsafe operations are those that potentially violate the memory-safety guarantees of Rust's static semantics."
  • EvilTak
    EvilTak over 6 years
    @kazemakase thank you, that is what I want to say. I'll edit the answer.
  • dhardy
    dhardy almost 6 years
    But surprisingly you can't (currently) use TryFrom when going the other way: github.com/rust-lang/rust/issues/49415