Rollup merge of #76114 - marmeladema:duration-saturating-ops, r=shepmaster

Add saturating methods for `Duration`

In some project, I needed a `saturating_add` method for `Duration`. I implemented it myself but i thought it would be a nice addition to the standard library as it matches closely with the integers types.

3 new methods have been introduced and are gated by the new `duration_saturating_ops` unstable feature:
* `Duration::saturating_add`
* `Duration::saturating_sub`
* `Duration::saturating_mul`

If have left the tracking issue to `none` for now as I want first to understand if those methods would be acceptable at all. If agreed, I'll update the PR with the tracking issue.

Further more, to match the behavior of integers types, I introduced 2 associated constants:
* `Duration::MIN`: this one is somehow a duplicate from `Duration::zero()` method, but at the time this method was added, `MIN` was rejected as it was considered a different semantic (see https://github.com/rust-lang/rust/pull/72790#issuecomment-636511743).
* `Duration::MAX`

Both have been gated by the already existing unstable feature `duration_constants`, I can introduce a new unstable feature if needed or just re-use the `duration_saturating_ops`.

We might have to decide whether:
* `MIN` should be replaced by `ZERO`?
* associated constants over methods?
This commit is contained in:
Ralf Jung 2020-09-12 10:43:08 +02:00 committed by GitHub
commit 7344f930c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 156 additions and 16 deletions

View File

@ -100,6 +100,7 @@
#![feature(doc_cfg)]
#![feature(doc_spotlight)]
#![feature(duration_consts_2)]
#![feature(duration_saturating_ops)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intrinsics)]

View File

@ -108,6 +108,34 @@ impl Duration {
#[unstable(feature = "duration_constants", issue = "57391")]
pub const NANOSECOND: Duration = Duration::from_nanos(1);
/// The minimum duration.
///
/// # Examples
///
/// ```
/// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::MIN, Duration::new(0, 0));
/// ```
#[unstable(feature = "duration_constants", issue = "57391")]
pub const MIN: Duration = Duration::from_nanos(0);
/// The maximum duration.
///
/// It is roughly equal to a duration of 584,942,417,355 years.
///
/// # Examples
///
/// ```
/// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
/// ```
#[unstable(feature = "duration_constants", issue = "57391")]
pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
/// Creates a new `Duration` from the specified number of whole seconds and
/// additional nanoseconds.
///
@ -450,6 +478,29 @@ impl Duration {
}
}
/// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`]
/// if overflow occurred.
///
/// # Examples
///
/// ```
/// #![feature(duration_saturating_ops)]
/// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
/// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
/// ```
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
#[inline]
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
pub const fn saturating_add(self, rhs: Duration) -> Duration {
match self.checked_add(rhs) {
Some(res) => res,
None => Duration::MAX,
}
}
/// Checked `Duration` subtraction. Computes `self - other`, returning [`None`]
/// if the result would be negative or if overflow occurred.
///
@ -485,6 +536,29 @@ impl Duration {
}
}
/// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`]
/// if the result would be negative or if overflow occurred.
///
/// # Examples
///
/// ```
/// #![feature(duration_saturating_ops)]
/// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1));
/// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN);
/// ```
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
#[inline]
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
pub const fn saturating_sub(self, rhs: Duration) -> Duration {
match self.checked_sub(rhs) {
Some(res) => res,
None => Duration::MIN,
}
}
/// Checked `Duration` multiplication. Computes `self * other`, returning
/// [`None`] if overflow occurred.
///
@ -515,6 +589,29 @@ impl Duration {
None
}
/// Saturating `Duration` multiplication. Computes `self * other`, returning
/// [`Duration::MAX`] if overflow occurred.
///
/// # Examples
///
/// ```
/// #![feature(duration_saturating_ops)]
/// #![feature(duration_constants)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2));
/// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
/// ```
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
#[inline]
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
pub const fn saturating_mul(self, rhs: u32) -> Duration {
match self.checked_mul(rhs) {
Some(res) => res,
None => Duration::MAX,
}
}
/// Checked `Duration` division. Computes `self / other`, returning [`None`]
/// if `other == 0`.
///

View File

@ -10,6 +10,8 @@
#![feature(core_private_diy_float)]
#![feature(debug_non_exhaustive)]
#![feature(dec2flt)]
#![feature(duration_constants)]
#![feature(duration_saturating_ops)]
#![feature(exact_size_is_empty)]
#![feature(fixed_size_array)]
#![feature(flt2dec)]

View File

@ -89,6 +89,16 @@ fn checked_add() {
assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None);
}
#[test]
fn saturating_add() {
assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
assert_eq!(
Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)),
Duration::new(1, 1)
);
assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
}
#[test]
fn sub() {
assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1));
@ -107,6 +117,17 @@ fn checked_sub() {
assert_eq!(zero.checked_sub(one_sec), None);
}
#[test]
fn saturating_sub() {
let zero = Duration::new(0, 0);
let one_nano = Duration::new(0, 1);
let one_sec = Duration::new(1, 0);
assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1));
assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999));
assert_eq!(zero.saturating_sub(one_nano), Duration::MIN);
assert_eq!(zero.saturating_sub(one_sec), Duration::MIN);
}
#[test]
#[should_panic]
fn sub_bad1() {
@ -136,6 +157,15 @@ fn checked_mul() {
assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None);
}
#[test]
fn saturating_mul() {
assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2));
assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3));
assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4));
assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000));
assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
}
#[test]
fn div() {
assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));

View File

@ -3,6 +3,7 @@
#![feature(const_panic)]
#![feature(duration_consts_2)]
#![feature(div_duration)]
#![feature(duration_saturating_ops)]
use std::time::Duration;
@ -15,29 +16,29 @@ fn duration() {
const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
const MAX_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
assert_eq!(MAX_ADD_ZERO, Some(MAX));
const MAX_CHECKED_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX));
const MAX_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
assert_eq!(MAX_ADD_ONE, None);
const MAX_CHECKED_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
assert_eq!(MAX_CHECKED_ADD_ONE, None);
const ONE_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
assert_eq!(ONE_SUB_ONE, Some(ZERO));
const ONE_CHECKED_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO));
const ZERO_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
assert_eq!(ZERO_SUB_ONE, None);
const ZERO_CHECKED_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
assert_eq!(ZERO_CHECKED_SUB_ONE, None);
const ONE_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
assert_eq!(ONE_MUL_ONE, Some(ONE));
const ONE_CHECKED_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE));
const MAX_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
assert_eq!(MAX_MUL_TWO, None);
const MAX_CHECKED_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
assert_eq!(MAX_CHECKED_MUL_TWO, None);
const ONE_DIV_ONE : Option<Duration> = ONE.checked_div(1);
assert_eq!(ONE_DIV_ONE, Some(ONE));
const ONE_CHECKED_DIV_ONE : Option<Duration> = ONE.checked_div(1);
assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE));
const ONE_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
assert_eq!(ONE_DIV_ZERO, None);
const ONE_CHECKED_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
assert_eq!(ONE_CHECKED_DIV_ZERO, None);
const MAX_AS_F32 : f32 = MAX.as_secs_f32();
assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32);
@ -50,6 +51,15 @@ fn duration() {
const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE);
assert_eq!(ONE_AS_F64, 1.0_f64);
const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE);
assert_eq!(MAX_SATURATING_ADD_ONE, MAX);
const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE);
assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO);
const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2);
assert_eq!(MAX_SATURATING_MUL_TWO, MAX);
}
fn main() {