(name/syntax to be bikeshedded; In this post I use
T as U as abbreviation of
let x: T; x as U).
as operator used for casting does one of two things:
- Lossless, reliable conversion to a compatible type of same or larger size (e.g.
u32 as u64).
- Potentially lossy and dangerous truncation to a smaller type (e.g.
usize as u8).
The problem is that I often only want the first one, but I have no way of preventing the second case from unintentionally happening.
Although unexpected integer overflow is “safe” per Rust’s strict definition, it’s still a problem that can cause bugs, data loss and panics in Rust code, and cause memory safety issues when wrong values are passed through FFI.
usize as u32 is correct on 32-bit architectures, but on 64-bit architectures it will compile without warnings to code that may be subtly broken (and vice-versa with
u64 as usize).
It’s easy to say “well, just be careful with types you cast”, but that’s not so easy in practice:
Sizes of aliased type names are non-obvious. How do you know whether
somelibrary::Distance as usizeis correct (lossless), on all platforms?
And you can’t know whether it’ll remain correct in v2.0 of the library (but the cast will, unfortunately, compile without warnings even when it becomes incorrect).
usizeand C types can vary, especially on architectures that I don’t test on
I suppose all of my Rust code would compile on 16-bit platforms, but would fail miserably at runtime. I’d prefer it to fail to compile on some architectures if some uses of
asthat were lossless on 64-bit became lossy on the smaller architecture, as the compile errors would help reviewing and fixing the code.
For integers Rust has overflow-checked and wrapping variants. I think of current
as operator as the wrapping variant of type conversion, and I’d like to have a variant that prevents unintentional overflows.