4 ms·
Nitpick: Rust doesn't panic in this situation, it saturates (i.e., returns the largest possible value for the given integer type). Among other reasons, because
by ameliaquining 2mo ago
Nitpick: Rust doesn't panic in this situation, it saturates (i.e., returns the largest possible value for the given integer type). Among other reasons, because a lot of existing programs that triggered this UB happened to work in practice, and would have experienced the panicking behavior as a severe regression.
This case also shows the limits of Rust's safety culture; they knew about the problem for a long time, and could have fixed it right away if they'd been willing to make programs that do a lot of float-to-int casts eat a performance regression, but a number of users objected strongly to this. So it remained unfixed until they figured out a way to make it fast enough that no one would really notice.
- dwattttt 2mo agoIt's interesting to consider that the same question would've surely come up during C and C++ language development, but that the answer is informed by the impact on the ecosystem, which was dramatically different when C, C++, and Rust were all being developed.
- tialaramex 2mo agoI don't think it would "surely come up". The C++ community is very comfortable with "Don't do that" as the lesson even though it's not actionable. Like I said, safety is cultural. You can invent this stuff, somebody did, but the way most people end up doing it isn't because they all spontaneously invented the same solution, it was absorbed from their culture. C++ culture says "Don't do that" all the time instinctively. An actual concrete objection to fixing it might be offered if you insist on one, but they start with "Don't do that". Sorting is my go-to example. Rust's sorts are safe. If I sort "Alligator", "Baboon", "Cat", "Donkey" then no matter what my ordering rule was nothing crazy happens. If my rule was nonsense, like "Every item is before every other item" then Rust might panic, it is allowed to do that - but otherwise it just will give me back the same four items but who knows what order because my ordering rule is nonsense. That seems easy enough, right? In C++ if my ordering rule does not meet the precise requirements of the C++ language then all bets are off, that's Undefined Behaviour. I might get back "Cat", "Cat", "Cat", "Cat" even though there was originally only a single cat, or it might scribble past the end of my list of animals, crash, or anything at all. And while it would be permissible for real C++ standard library implementations to do better, on the whole they don't. "Don't do that" is the answer if you ask why this defect is allowed.
- tialaramex 2mo ago> Rust doesn't panic in this situation, it saturates Yes sorry, in my head I'm thinking about what I'd want here because I do not like any of Rust's 'as' casts and in fact the thing I'd want here (TryInto) just does not exist on purpose for this reason. I wonder if I've ever run into this, realised I can't write a TryInto and if so what did I end up doing - interesting.
- ameliaquining 2mo agoIt's actually quite straightforward to write a TryFrom impl that returns Err if the conversion fails (i.e., if the input floating-point value is infinite, NaN, or out of range of the target integer type). The reason this hasn't been done is, what if the input is within range but has a nonzero fractional part? Do you truncate or round (meaning the conversion can sometimes be lossy), or do you return Err unless the conversion can be done losslessly? It's not obvious which behavior is right, but they'd have to pick one.
- tialaramex 2mo agoSure. These decisions are tricky, I had this concern in `realistic` too. My `Rational` type is the "big rationals" (the finite subset of rational numbers I can represent with your available RAM) and these are certainly able to precisely represent any 32-bit or 64-bit float which isn't NaN or an infinity. However, vice versa is not true of course. 0.1 is a very easy Rational, but of course binary floating point cannot represent this exactly. In the end I punted, TryInto<Rational> is implemented for f32 and f64 but the opposite is not provided at all.