Rust provides convenient access to several different ways of doing arithmetic:
a + b: shouldn't overflow but this is not checked in all configurations
a.wrapping_add(b): overflow is explicitly fine
a.saturating_add(b): saturate the result on overflow
There are also a.checked_add(b) and a.overflowing_add(b) but those are significantly different: they are not of the shape fn(T< T) -> T, so one cannot easily use them in larger arithmetic expressions.
What is missing is the natural dual to a.wrapping_add(b): arithmetic that is guaranteed to panic on overflow. One can write a.checked_add(b).unwrap(), but the extra unwrap makes this very verbose, in particular for larger operations:
a
.checked_add(b.checked_mul(2).unwrap())
.unwrap()
.checked_add(c)
.unwrap()
.checked_mul(3)
.unwrap()
or
a
.checked_add(b.checked_mul(2).unwrap())
.and_then(|x| x.checked_add(c))
.and_then(|x| x.checked_mul(3))
.unwrap()
Motivating examples or use cases
There are parts of Miri where I want all arithmetic to be definitely overflow-checked, and currently we have code like in the example above, and it is an eyesore.
I find it surprising that we make it easier to write "guaranteed to wrap" arithmetic than "guaranteed to panic on overflow" arithmetic. That seems to be the exact opposite of where we actually want to steer people towards! (We have a flag to globally make all regular arithmetic panic-on-overflow, of course. But that's global for the entire binary [not even just a crate!] and e.g. not an option for Miri for performance reasons.)
Solution sketch
I propose we give "panic on overflow"-style arithmetic the same status as "wrap on overflow"-style arithmetic, and provide a nice operation with the type fn(T, T) -> T for that. I propose we call this strict_add, but the name is obviously open to bikeshedding. This applies not just to add but to all arithmetic operations that have a checked_ variant.
a
.strict_add(b.strict_mul(2))
.strict_add(c)
.strict_mul(3)
We could furthermore also add a Strict<i32> type that is similar to Wrapping<i32> and Saturating<i32>, but uses guaranteed overflow-checked arithmetic. But this is an optional extension of the ACP; having the methods as a first step would already be useful.
((Strict(a) + (Strict(b) * 2) + c) * 3).0
(This assumes that we have impl Add<i32> for Strict<i32> etc, which we currently do not seem to have for Wrapping, to my surprise.)
Alternatives
Other possible names: panicking_add, and a Panicking<i32> type. Or maybe something like unwrap_add or assert_add?
An alternative to Strict<T> as a newtype around T would be
enum Checked<T> {
Value(T),
Overflow,
}
and then having operator overloading for Checked<i32> etc. That wouldn't provide strict_* methods but (with enough operator overloading) one could write
((Checked::new(a) + (Checked::new(b) * 2) + c) * 3).unwrap()
We could do nothing, and people have to keep writing tons of unwrap everywhere. We could leave it to a library crate to provide this feature.
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
We think this problem seems worth solving, and the standard library might be the right place to solve it.
We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Proposal
Problem statement
Rust provides convenient access to several different ways of doing arithmetic:
a + b
: shouldn't overflow but this is not checked in all configurationsa.wrapping_add(b)
: overflow is explicitly finea.saturating_add(b)
: saturate the result on overflowThere are also
a.checked_add(b)
anda.overflowing_add(b)
but those are significantly different: they are not of the shapefn(T< T) -> T
, so one cannot easily use them in larger arithmetic expressions.What is missing is the natural dual to
a.wrapping_add(b)
: arithmetic that is guaranteed to panic on overflow. One can writea.checked_add(b).unwrap()
, but the extraunwrap
makes this very verbose, in particular for larger operations:or
Motivating examples or use cases
There are parts of Miri where I want all arithmetic to be definitely overflow-checked, and currently we have code like in the example above, and it is an eyesore.
I find it surprising that we make it easier to write "guaranteed to wrap" arithmetic than "guaranteed to panic on overflow" arithmetic. That seems to be the exact opposite of where we actually want to steer people towards! (We have a flag to globally make all regular arithmetic panic-on-overflow, of course. But that's global for the entire binary [not even just a crate!] and e.g. not an option for Miri for performance reasons.)
Solution sketch
I propose we give "panic on overflow"-style arithmetic the same status as "wrap on overflow"-style arithmetic, and provide a nice operation with the type
fn(T, T) -> T
for that. I propose we call thisstrict_add
, but the name is obviously open to bikeshedding. This applies not just toadd
but to all arithmetic operations that have achecked_
variant.We could furthermore also add a
Strict<i32>
type that is similar toWrapping<i32>
andSaturating<i32>
, but uses guaranteed overflow-checked arithmetic. But this is an optional extension of the ACP; having the methods as a first step would already be useful.(This assumes that we have
impl Add<i32> for Strict<i32>
etc, which we currently do not seem to have forWrapping
, to my surprise.)Alternatives
Other possible names:
panicking_add
, and aPanicking<i32>
type. Or maybe something likeunwrap_add
orassert_add
?An alternative to
Strict<T>
as a newtype aroundT
would beand then having operator overloading for
Checked<i32>
etc. That wouldn't providestrict_*
methods but (with enough operator overloading) one could writeWe could do nothing, and people have to keep writing tons of
unwrap
everywhere. We could leave it to a library crate to provide this feature.Links and related work
What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: