Yes, it would. However, it rarely does because of how the incentives are: it's useful for optimization to pretend that UB never happens, and more optimization leads to faster binaries, which are what the compiler engineers often pursue.
Right, but given that panicing was already allowable behavior, why was the new behavior chosen to be one likely to introduce subtle bugs? It seems much better to loudly proclaim the existence of an erroneous precondition, which is consistent with how things like array indexing behave.
I guess I’ll have to go dig up the RFC discussion on this one; it should make for interesting reading.
1. Panicing is not in line with how `as` casts are supposed to act. (e.g. `u32value as u8` does not panic but just takes the "lower" one byte.)
2. This might (I haven't profiled it) introduce performance regressions in ways which should not happen.
3. Besides in some usages around `dyn` other usages of `as` get increasingly more alternatives. It's just a question of time until `as` (for int/float casts) is recommended to not be used at all, maybe even linted against.
4. Given precedence of many other programming languages people don't expect a "simple" float to int cast to be failable. (The new methods replacing `as` make the fallibility clear, as it's e.g. `u64::try_from(bigf64)`).
5. It's udef-ness is only detected/handled in llvm, _I don't know_ if llvm provides similar well integrated mechanisms for this as it does for integer overflows. If not that would be another problem.
I came here with same question as davrosthedalek "Why doesn't it just panic?" and yours is an excellent and very convincing answer. Thanks for writing it up.
That phrasing seemingly implies that there was a generally-agreed intended behaviour for that case that was unfortunately neither documented nor actually exhibited. But that was not the case. The overflow behaviour of float-to-int casts was undefined, full stop. There was some consensus as to what such casts should not do, but no agreement on what the actual behaviour should be. Eventually, the discussion settled on defining casting overflow to saturate, and the implementation was altered to match.
> Right, but given that panicing was already allowable behavior, why was the new behavior chosen to be one likely to introduce subtle bugs?
Because casting to floats is not UB in the Rust spec, it's UB in LLVM. That's the whole reason this was an issue in the first place.
Now, Rust could have chosen to define the behavior to panic, but so far it's been a hard and fast rule in Rust that as casts do not panic. You would have to have a much better reason to change that then "well, it was UB before" since (1) nobody wanted it to be UB before, and (2) the actual implementation never panicked (and people absolutely rely on the fact that casts don't panic in unsafe code).
I thing you are right about that, I'd prefer panicking too. Also, reading the RFC will surely clear up the motivation.
Without knowing this case, I'd wager a guess: it's about performance. Panicking introduces a branch and side-effects which, again, affects negstively optimization potential and performance. The saturating cast affects performance too, but less. If some old code has a lot of number crunching containing these operations, a big performance regression would be nasty.
I think the long-term plan is to deprecate `as` entirely (probably in a future edition). You will then be free to pick between a function that panics, one that gives you a Result, or that saturates, etc. I believe most if not all of these functions already exist.
It looks like ‘as’ is generally defined as a truncating cast operator for other numeric types. Since it doesn’t panic for other overflow situations (like u64->u32), they chose consistency with those cases.
Compilers are better at eliding array index checks than about eliding numeric overflow checks; an ordinary function that operates on arrays will often be able to reasonably determine both the length of the array and the scope of the indexing variable (Rust's iterators are very good at this), whereas an ordinary math function often has almost no information that usefully limits which values it might be getting called with.
I don't disagree that making more obvious errors into panics would be nice, but the performance implications are often quite unforgiving.
They aren't unforgiving, because if you need performance you can use another function that does not do the check. Same as for arrays.
It seems strange to have a cast as a safe operation and yet return results that are almost surely a bug. This means I will avoid casts altogether in my code, and I do hope they get rid of them at some point as some have suggested.