Where is std::num::Saturating? (Going to pre-RFC!)

Isn't it ironic Checked<u32>/Overflowing<u32> in practice will take as much stack as an u64? :slight_smile:
...and Checked<u64>/Overflowing<u64> on x86-64 would probably mean 3 * 4 = 12 bytes on the stack taking alignment into account?.. so in effect as much as a hypothetical u96?..

1 Like

I don't think it is a good idea to implement such a trait on (T, bool) or treat an Option as a numeric type. I'd say it twists the purpose of the type system and the idea of representing distinct things and concepts with dedicated types.

I didn't get the example. Why would anybody want to write any spaghetti code instead of using a proper abstraction? We can be a debate whether an abstraction is a good or not, whether it is well-suited for a particular problem or something better should be used and so on… but I hope we can agree that it is reasonable to save a developer of writing repetitive and lengthy code – code generation is the compiler's job and it can handle it better.

I already commented the case of Overflowing. TL;DR: This one seems to be the least usable, IMO, and I don't mean defend it unless someone comes with a good case. But the others seem to be fairly useful.

The concern about the evaluation order is valid though and I comment it below.

I definitely agree it should be emphasized. However, it is actually the case even for the built-in types:

let a = u32::MAX;
let b = "1".parse::<u32>().unwrap();
println!("{}", a + b - b);

Ouch! It panics! Well, it panics in debug… it does not panic in release :thinking: How unexpected :confused:

Alright, I wanted just to point out that it is good not to forget that common mathematical truths do not apply to numbers with limited precision and/or range (and it is not just associativity). These types are not special in this sense, they perhaps make the fact more visible – which I would consider good actually. Whoever uses them, uses them on purpose, I guess, fully aware of the limited range of the underlying types and just wants to have a bit more control.

Yes, which is exactly why I said there should be wrappers if we want overloaded operators (in addition to the fact that a library couldn't impl std traits on std/builtin types even if it wanted to). Also, I agree that unlike Option, which has a "natural" monad instance, (T, bool) has no such natural interpretation (indeed I think that the overflowing_ methods should probably been written to return a named type instead, but too late for that now).

1 Like

Checked handles the case of "I want to gracefully handle overflow". What about if you want to panic on overflow, as currently happens in debug mode, but do so regardless of compiler flags? It'd be nice to have a wrapper type that doesn't introduce Option and require .expect calls everywhere.

4 Likes

I was thinking about it, but it seems to me that Checked fits the usual problem handling in Rust better – I mean: if something fails, Result comes to record the fact and lets the developer to deal with that… unwrap/expect being just a possibility and often not the best one. This is fairly close.

The other assumption might be disproved by the experience of others, but I assume that Checked would be used in expressions that are a little bit more complex than just a + b and, I guess, it is fine to simply write the whole expression and perform panicking unwrap at the end. IMO, this is not worse than letting a Result panic. I also assume that the compiler can optimize the expression evaluation, so that as soon as an operand becomes a wrapped None, it can jump to the unwrapping point and let the program panic.

Anyway, if my assumption is wrong, I think such a type could be proposed as well. Not sure about its name though. Suggestions?

Finally, I think that the most ergonomic construct would be something like the macro mentioned in the Future possibilities appendix. Especially in this one case it looks quite right to me (and ergonomic as well):

let r = strict! { a + b };

I'm not sure how much feasible it is without the compiler's support. But… I kind like such an option in general, as I mentioned before. It could even supersede these wrapping types completely. Who would actually use them explicitly with such a construct? I'll have to try finding similar older proposals of something similar – if anybody knows about some, please, tell. I'd like to know what objections against such a construct were raised.

I was actually missing a Saturating type while writing a partial implementations of C++'s std::chrono in rust. I needed to satisfy two (unsafe) marker traits, TrivialClock and Monotonic, and doing just plain math has the possibility of panicking (which TrivialClock prohibits for the particular method), and Wrapping could violate Monotonic (because it requires that TimePoints returned from consecutive calls be increasing). I ended up having to write a hack for soundness, fittingly called HackForSoundness, which did exactly what Saturating would do (I didn't particularily care, I just didn't want to.

1 Like

A very old but related issue onto which I just stumbled:

1 Like

For what it's worth this fits my cryptographic usages better than returning a Result. In the case of what I'm doing, I want panic-on-overflow as a sort of belt-and-suspenders approach to making sure that even if the rest of the code screws up and causes an overflow, that that overflow won't accidentally result in a disastrous cryptographic failure.

In some cases it would actually be preferable to catch those failures eagerly, rather than tainting the internal state of a Checked type only to discover it overflowed later trying to consume the result.

3 Likes

That's very much my use case as well.

2 Likes

Hi everyone. Can we manage it without wrapper? Thanks

Try overflower.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.