6 ms·
I'm not sure I understand this. Does it not produce a run time error? Why not? This looks very dangerous, because it essentially does the "nearest to right" th
by davrosthedalek 6y ago
I'm not sure I understand this. Does it not produce a run time error? Why not?
This looks very dangerous, because it essentially does the "nearest to right" thing. Say, you cast 256 to a u8, it's then saturated to 255. That's almost right, and a result might be wrong only by 0.5%. Much harder to detect than if it is set to 0.
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- tinalumfoil 6y agoMost floating points aren't integers anyway, so if you think of it as casting 256 to the nearest u8 it's correct, same as rounding 0.25 to 0. NaN to 0 is a bit more concerning, but inconvienence and compatibility need to be weighed against catching every error (no type system will catch every bug).
- pwdisswordfish2 6y ago> I'm not sure I understand this. Does it not produce a run time error? Why not? It’s not supposed to. Type casting with ‘as’ is supposed to be lightweight and always succeed; there is no room in the type system to return an error. In case lossless casting is not possible, some value still has to be returned. Until now, this was outright UB — meaning the compiler is not even obligated to keep it consistent from one build to another. Saturating, while still not optimal, is at least deterministic. > This looks very dangerous, because it essentially does the "nearest to right" thing. That’s why the intention is to introduce more robust approximate conversion functions and eventually probably deprecate ‘as’ casts altogether. There has been a number of discussions about this; current disagreements seem to be about how to handle the various possible rounding modes.
- chowells 6y ago> meaning the compiler is not even obligated to keep it consistent from one build to another. Way worse than that. The compiler wasn't obligated to act like anything at all. It would be totally legal to compile it so that the first time the value was accessed you got 0, the next time you got 1 - within the same program execution, with no mutation of the value. That is the sort of thing that is observed behavior of UB in the worst cases, and why it's so terrible to just pretend that UB is innocuous.
- Sharlin 6y agoWay worse than that, even. UB poisons every state of the program that eventually results in UB. For example, the optimizer is well within its rights to remove as dead code any branch that, if taken, would provably lead to UB at some arbitrary future point of execution.
- TomMarius 6y agoThat could literally produce no output program?
- rjeli 6y agoc.f. “nasal demons”
- Spivak 6y agoYep! Dumb example. main() x = get_from_some_external_data_source() if x: print("Hello World") trigger_ub() You might expect this code to always print if x is true but the optimizer can look at this and say "welp, if x is true then it would trigger ub, therefore it must be false, and since x must always be false we can just remove that entire branch."
- kbenson 6y agoMy favorite example along these lines (in C) is "Cap'n'Proto remote vuln: pointer overflow check optimized away by compiler"[1] which was covered here a few years back and shows all of these "theoretical" compiler behaviors coming to a head in a real bug which is thoroughly explained. 1: https://news.ycombinator.com/item?id=14163111 https://news.ycombinator.com/item?id=14163111
- kd5bjo 6y agoAs it was UB anyway, wouldn’t the compiler also be within its rights to detect the problem and panic the thread?
- GolDDranks 6y agoYes, 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.
- kd5bjo 6y agoRight, 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.
- GolDDranks 6y agoI 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.
- kd5bjo 6y agoIt 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. https://github.com/rust-lang/rust/issues/10184#issuecomment-194885126 https://github.com/rust-lang/rust/issues/10184#issuecomment-...
- jfkebwjsbx 6y ago> there is no room in the type system to return an error There is: you panic, like in the array case. That would have been much robust (at the cost of performance).
- kibwen 6y agoFor consistency you would also expect `257u32 as u8` to panic (which it doesn't, and never has); the `as` operator has always been about fast-and-lossy conversions, with the standard library ideally providing methods for more principled conversions.
- quietbritishjim 6y agoSeems a pity they didn't add a built-in at the same time that's a bit more nuanced. It could maybe return an enum with Success(value), Underflow, Overflow and NaN. That way it's up to the coder to decide whether they want a saturating cast or to check the result explicitly.
- estebank 6y agoThere are conversations about doing exactly that with `.try_into()` doing exactly that.
- kibwen 6y ago> Why not? For better or worse, Rust 1.0 released with the philosophy that the `as` operator is for "fast and loose" conversions where accuracy is not prioritized; e.g. casting a u32 to a u8 would always risk silently truncating in the event the value was too large to represent. Over the years the language has added a lot of standard library support for bypassing the `as` operator entirely, and I think the prevailing opinion at this point might be that if they had the to do it all over again they might not have had the `as` operator at all, instead making do with a combination of ordinary error-checked conversion methods and the YOLO unsafe unchecked methods as seen here. Which is to say: it's not that they technically couldn't have gone with the panic approach, but (performance implications aside) I think they'd rather just start moving away from `as` in general wherever possible.
- yazaddaruvala 6y agoShould `as` then be a candidate for deprecation in the next edition?
- kibwen 6y agoI don't know about the next edition; `as` is also used for a small number of non-numeric conversions, like turning a reference into a raw pointer or for creating a trait object. In order to completely remove `as`, you would need to first introduce alternatives to those (the trait object use case specifically might be a bit weird as a method rather than a keyword, but I don't know if that's a big problem). Alternatively one could just deprecate `as` for numeric conversions, although there are still some library holes that would need to be patched up (e.g. other than `as` I don't know of an existing single method to say that you want a fast integer conversion routine that merely truncates a u64 into a u32; right now the alternative is `try_into`, which does a runtime check and returns a Result). It might also exacerbate tensions with some people who for quite a while have been grumpy that Rust requires frequent explicit numeric conversions when doing things like indexing (which always requires a `usize` type, so you see `foo[bar as usize]` unless people want to always work with usizes directly); would these people be happier if that were `foo[bar.as_usize()]` instead, or would this all be blocked on a discussion about being more lenient with numeric conversions?