7 ms·
> Some Rust developers argue that even "size as i32" should be avoided, and "size.try_into()" should be used instead, since it forces the programmer to treat an
by derefr 5y ago
> Some Rust developers argue that even "size as i32" should be avoided, and "size.try_into()" should be used instead, since it forces the programmer to treat an overflow explicitly at runtime, instead of silently wrapping.
It's important to still have the option for efficient truncating semantics, though; some software (e.g. emulators) needs to chunk large integers into 2/4/8 smaller ones, and rotation + truncating assignment is usually the cheapest way to do that.
But, importantly, this is a rare case. Most software that does demoting casts does not mean to achieve these semantics.
So I wonder — are there any low-level/systems languages where a demoting cast with a generated runtime check gets the simple/clean syntax-sugared semantics (to encourage/favor its use), while truncating demotion requires a clumsier syntax (to discourage its use)?
- titzer 5y agoIn Virgil, casts are written "type.!(expr)". Casts between numbers check ranges and roundability (for float<->int conversion). Reinterpreting the bits is written "type.view(expr)" and ignores signs, looks at the raw float bits, etc. edit: a cast will throw an exception if it fails, in case that was not clear from context.
- londons_explore 5y agoIt would be nice if CPU's had an instruction for "read the low 8 bits of this register, and require the high bits all be zeros (otherwise throw an exception)". Then safety is free...
- derefr 5y agoWhat does "otherwise throw an exception" mean at the CPU level? I know Erlang's BEAM VM has a "fail jump pointer register", where instructions that can fail have relative-jump offsets encoded as immediates for those instructions, and if the instruction "fails" in whatever semantic sense, it takes the jump. But most CPUs don't have anything like that. Would you want it to trap, like with integer division by zero? CPU traps are pretty hard to handle in most language runtimes, such that most compilers generate runtime checks to work around them, rather than attempting to handle them.
- deleted 5y ago[deleted]
- simias 5y agoI assume they meant throwing an exception like divisions by zero usually do, i.e. a hardware trap. I always thought that overflows should be checked in hardware, I suppose it's not a stretch to extend that to truncation. It's controversial though, and obviously mostly a thought experiment anyway unless we manage to convince some CPU manufacturer to extend their ISA that way. MIPS does have (optional) trapping overflow on signed add/sub overflow, so at least there's a small precedent for it.
- ridiculous_fish 5y agoSwift checks all arithmetic by default: https://swift.godbolt.org/z/rW614G5aq https://swift.godbolt.org/z/rW614G5aq It seems obvious that future Apple CPUs will have hardware support for this, if they don't already.
- saagarjha 5y agoI don’t see this happening unless it makes it into the ARM ISA.
- gumby 5y agoI don’t know the terms of Apple’s license with ARM, do you? I’m quite interested. Given that Apple was one of the original founders of ARM it’s quite possible that their license allows much more latitude anyone else’s.
- saagarjha 5y agoAdding new instructions to userspace programs is almost certainly not going to fly. All of their extensions have been hidden behind an opaque API, or limited to use in the kernel.
- tialaramex 5y agoRight, this is a small infelicity in Rust, it is easier to write let x = size as i32; ... even if what you meant was closer to let x: i32 = size.try_into().expect("We are 100% sure size is small enough to fit into x"); But at least it isn't C or C++ where you might accidentally write x = size; ... and the compiler doesn't even warn you that size is bigger than x and you need to think about what you intended. It's really hard to fix this in C++. Some of the Epoch proponents want to do so using epochs to get there, basically you'd have a "new" epoch of C++ in which narrowing must be explicit, and old code would continue to have implicit narrowing so it doesn't break.
- dkirill 5y ago> and the compiler doesn't even warn you that size is bigger than x That's not true tho, compiler with reasonable flags set will definitely warn you and if you really don't like this kind of code you can force compiler to issue an error instead
- simias 5y agoWhat flags you have in mind? Because this code doesn't generate any warning with GCC 11.1.0 with -Wall -Wextra: int main(void) { int some_int = 1234567; char c = some_int; return c; }
- ridiculous_fish 5y ago-Wconversion will do it.
- aaronmdjones 5y ago-Wall -Wextra -Wpedantic does not enable all diagnostics. This is GNU's idea of "all". Contrast to Clang's -Weverything, which will.
- 130e13a 5y agokeep in mind though that -Weverything is not intended to be used in production: https://quuxplusone.github.io/blog/2018/12/06/dont-use-weverything/ https://quuxplusone.github.io/blog/2018/12/06/dont-use-wever...
- dathinab 5y agoAs a interesting side note: The "as" operator is often considered to have been a mistake. Both because of unchecked casts and because of "doing to much". So I wouldn't be surprised if in the (very) long term there will be a rust edition deprecating `as` casts (after we have alternatives to all cast done with `as`, which are: Pointer casts, dyn casts/explicit coercion and truncating integer casts, for some we already have alternatives for on stable for other not). And for all who want to not have `as` today you can combine extension traits (which internally still use `as`) + clippy lint against any usage of `as`. EDIT: I forgot widening integer casts in the list above ;-).
- eloff 5y agoI would prefer not to see that happen, I'm fine with as and the safer options as they are currently. It would be a big job to update all the code in the wild when you want to move to the newer edition.
- wizzwizz4 5y agoYou don't need to do that. Rust editions are fully backwards-compatible, since they can depend on code from different editions.
- eloff 5y agoI'm aware of that, which is why I specified when you want to move to the newer edition.
- tialaramex 5y agoI don't see it as a big job. Am I wrong? Imagine there's a suitable narrow::<type>() function introduced which has the same consequence, always narrowing, if your data was too wide it may drop important stuff on the floor, and narrow() just says that's too bad. Rust 2030 can introduce narrow::<type>(), warn for narrowing as usage and then Rust 2035 can error for as. The Rust 2030 -> 2035 conversion software can consume code that does { x as y } and write { x.narrow::<y>() } instead. This code is not better but it's still working in Rust 2035 and this explicit narrow() function is less tempting for new programmers than as IMO.
- frogblast 5y agoIn swift: // a is a UInt64 let a = Int.random(in: 0..<Int.max) // causes a fatal runtime error if out of range, halts execution let b = UInt32(a) // returns a UInt32?, which will be nil if out of range let c = UInt32(exactly: a) // another approach for exact conversion: guard let d = UInt32(exactly: a) else { // conversion failed // handle error and return return } // 'd' is a UInt32 without any bits lost // always succeeds, will return a UInt32, either clamped or truncated. Truncation just cuts off the high bits. let e = UInt32(clamping: a) let f = UInt32(truncatingIfNeeded: a)
- chippiewill 5y ago> are there any low-level/systems languages where a demoting cast with a generated runtime check gets the simple/clean syntax-sugared semantics (to encourage/favor its use), while truncating demotion requires a clumsier syntax (to discourage its use)? Not exactly the same thing, but in a related area C++ does this a bit. In C++ you can always still do a c-style cast `(int) some_var` (and the implicit casts obviously), but in general you're meant to use the C++ style explicit casts like `static_cast` and `const_cast`. These are generally tidy, but the most powerful and dangerous of these casts is deliberately awkwardly named as `reinterpret_cast<int>(some_var)` rather than something terse. It's always easy to spot during a code review.
- gumby 5y agoYou can have your compiler warm about c-style casts