Because it seems that more people would like to close the gap, I prepared a pre-RFC. Thanks for your comments in advance.
- Feature Name:
missing_integer_wrappers
- Start Date: 2020-10-10
- RFC PR:
- Rust Issue:
Summary
This RFC proposes the addition of Saturating
, Overflowing
and Checked
types in the std::num
module.
Motivation
For convenience, the std::num
module provides a Wrapping
type.
This type improves ergonomy of wrapping arithmetics.
However, the remaining operation modes (saturating, overflowing and checked) lack similar support.
This RFC suggests to close this gap and make this part of the std::num
module complete.
According to this discussion on Rust Internals, closing the gap would be appreciated. For instance, checked operations are considered essential for secure implementations of many types of cryptography.
Guide-level explanation
The default semantics of integer arithmetic operations consider an overflow to be an error and panics in debug mode.
Built-in integer types provide methods for altenative semantics, which provide better control of the outcome.
For instance wrapping_add
performs wrapping (modular) addition, or checked_add
returns an Option
with the None
variant when the result overflows.
However, using these methods can be cumbersome and worse readable than arithmetic operators.
The Wrapping
, Saturating
, Overflowing
and Checked
types allow natural use of arithmetic operators with alternative semantics (as indicated by their names):
let value = (Checked(u32::MAX) + Checked(2)) / Checked(8);
// The computation encountered an overflow, so the overall result is None
assert_eq!(None, value.0);
Reference-level explanation
The Saturating<T>
can be almost identical to the existing Wrapping<T>
type, it just has to delegate to T::saturating_*
methods instead of T::wrapping_*
.
The Checked<T>
type wraps an Option<T>
type:
pub struct Checked<T>(pub Option<T>);
It delegates to T::checked_*
methods if none operand of the operation is None
.
If an operand is None
or the delegated method invocation returns None
, the result wraps None
as well.
The Overflowing<T>
type wraps (T, bool)
:
pub Overflowing<T>(pub T, pub bool);
It delegates to T::overflowing_*
methods and just wraps their result, hence the bool
component tracks whether an overflow occurred.
Drawbacks
It increases the size of the standard library.
Rationale and alternatives
All the types should provide similar user experience and features. Therefore the described design follows closely the existing implementation and avoids proposals that would affect it.
For the Overflowing
type an alternative representation could be:
pub Overflowing<T>(pub Result<T, T>);
The Err
variant carries the result that overflew.
This approach allows leveraging the feature-rich Result
type.
The disadvantage is the difference from the return type of overflowing_*
methods.
It is worth mentioning some features of the Checked
type implemented in the checked
crate:
- It implements the
From
trait as a convenient alternative toChecked(Some(number))
. - It implements the
Deref
andDerefMut
traits to return a reference to the wrappedOption
. - It implements binary operators that take a built-in integer as well.
Implementing the From
trait seems to be harmless and it should be probably implemented for the Wrapping
type anyway.
It would provide the same means for wrapping an integer value, regardless of the actual structure of the wrapping type.
Implementing Deref
, DerefMut
and the binary operators is more questionable as the boundaries between arithmetic modes become less visible.
And while it yet works for the Checked
type, because the wrapped Option
type can't be confused with a numeric type, it is not viable for the Wrapping
and Saturating
types.
Prior art
std::num::Wrapping
-
checked
crate
Future possibilities
It might be worth implementing macros for a scoped change of the arithmetic mode:
let value = checked! { a + b * foo(x) }.ok().map(|n| overflowing! { n + 1 })?;
The operands in the expressions are wrapped in the appropriate wrapper type.
Here the advantage of using the Result
type for the Overflowing
wrapper are clearly visible.