Skip to content

Commit 7344f93

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#72790 (comment)). * `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?
2 parents 0f5c769 + b869aa5 commit 7344f93

File tree

5 files changed

+156
-16
lines changed

5 files changed

+156
-16
lines changed

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
#![feature(doc_cfg)]
101101
#![feature(doc_spotlight)]
102102
#![feature(duration_consts_2)]
103+
#![feature(duration_saturating_ops)]
103104
#![feature(extern_types)]
104105
#![feature(fundamental)]
105106
#![feature(intrinsics)]

library/core/src/time.rs

+97
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,34 @@ impl Duration {
108108
#[unstable(feature = "duration_constants", issue = "57391")]
109109
pub const NANOSECOND: Duration = Duration::from_nanos(1);
110110

111+
/// The minimum duration.
112+
///
113+
/// # Examples
114+
///
115+
/// ```
116+
/// #![feature(duration_constants)]
117+
/// use std::time::Duration;
118+
///
119+
/// assert_eq!(Duration::MIN, Duration::new(0, 0));
120+
/// ```
121+
#[unstable(feature = "duration_constants", issue = "57391")]
122+
pub const MIN: Duration = Duration::from_nanos(0);
123+
124+
/// The maximum duration.
125+
///
126+
/// It is roughly equal to a duration of 584,942,417,355 years.
127+
///
128+
/// # Examples
129+
///
130+
/// ```
131+
/// #![feature(duration_constants)]
132+
/// use std::time::Duration;
133+
///
134+
/// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
135+
/// ```
136+
#[unstable(feature = "duration_constants", issue = "57391")]
137+
pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
138+
111139
/// Creates a new `Duration` from the specified number of whole seconds and
112140
/// additional nanoseconds.
113141
///
@@ -450,6 +478,29 @@ impl Duration {
450478
}
451479
}
452480

481+
/// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`]
482+
/// if overflow occurred.
483+
///
484+
/// # Examples
485+
///
486+
/// ```
487+
/// #![feature(duration_saturating_ops)]
488+
/// #![feature(duration_constants)]
489+
/// use std::time::Duration;
490+
///
491+
/// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
492+
/// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
493+
/// ```
494+
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
495+
#[inline]
496+
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
497+
pub const fn saturating_add(self, rhs: Duration) -> Duration {
498+
match self.checked_add(rhs) {
499+
Some(res) => res,
500+
None => Duration::MAX,
501+
}
502+
}
503+
453504
/// Checked `Duration` subtraction. Computes `self - other`, returning [`None`]
454505
/// if the result would be negative or if overflow occurred.
455506
///
@@ -485,6 +536,29 @@ impl Duration {
485536
}
486537
}
487538

539+
/// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`]
540+
/// if the result would be negative or if overflow occurred.
541+
///
542+
/// # Examples
543+
///
544+
/// ```
545+
/// #![feature(duration_saturating_ops)]
546+
/// #![feature(duration_constants)]
547+
/// use std::time::Duration;
548+
///
549+
/// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1));
550+
/// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN);
551+
/// ```
552+
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
553+
#[inline]
554+
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
555+
pub const fn saturating_sub(self, rhs: Duration) -> Duration {
556+
match self.checked_sub(rhs) {
557+
Some(res) => res,
558+
None => Duration::MIN,
559+
}
560+
}
561+
488562
/// Checked `Duration` multiplication. Computes `self * other`, returning
489563
/// [`None`] if overflow occurred.
490564
///
@@ -515,6 +589,29 @@ impl Duration {
515589
None
516590
}
517591

592+
/// Saturating `Duration` multiplication. Computes `self * other`, returning
593+
/// [`Duration::MAX`] if overflow occurred.
594+
///
595+
/// # Examples
596+
///
597+
/// ```
598+
/// #![feature(duration_saturating_ops)]
599+
/// #![feature(duration_constants)]
600+
/// use std::time::Duration;
601+
///
602+
/// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2));
603+
/// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
604+
/// ```
605+
#[unstable(feature = "duration_saturating_ops", issue = "76416")]
606+
#[inline]
607+
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
608+
pub const fn saturating_mul(self, rhs: u32) -> Duration {
609+
match self.checked_mul(rhs) {
610+
Some(res) => res,
611+
None => Duration::MAX,
612+
}
613+
}
614+
518615
/// Checked `Duration` division. Computes `self / other`, returning [`None`]
519616
/// if `other == 0`.
520617
///

library/core/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#![feature(core_private_diy_float)]
1111
#![feature(debug_non_exhaustive)]
1212
#![feature(dec2flt)]
13+
#![feature(duration_constants)]
14+
#![feature(duration_saturating_ops)]
1315
#![feature(exact_size_is_empty)]
1416
#![feature(fixed_size_array)]
1517
#![feature(flt2dec)]

library/core/tests/time.rs

+30
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ fn checked_add() {
8989
assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None);
9090
}
9191

92+
#[test]
93+
fn saturating_add() {
94+
assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
95+
assert_eq!(
96+
Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)),
97+
Duration::new(1, 1)
98+
);
99+
assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
100+
}
101+
92102
#[test]
93103
fn sub() {
94104
assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1));
@@ -107,6 +117,17 @@ fn checked_sub() {
107117
assert_eq!(zero.checked_sub(one_sec), None);
108118
}
109119

120+
#[test]
121+
fn saturating_sub() {
122+
let zero = Duration::new(0, 0);
123+
let one_nano = Duration::new(0, 1);
124+
let one_sec = Duration::new(1, 0);
125+
assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1));
126+
assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999));
127+
assert_eq!(zero.saturating_sub(one_nano), Duration::MIN);
128+
assert_eq!(zero.saturating_sub(one_sec), Duration::MIN);
129+
}
130+
110131
#[test]
111132
#[should_panic]
112133
fn sub_bad1() {
@@ -136,6 +157,15 @@ fn checked_mul() {
136157
assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None);
137158
}
138159

160+
#[test]
161+
fn saturating_mul() {
162+
assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2));
163+
assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3));
164+
assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4));
165+
assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000));
166+
assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
167+
}
168+
139169
#[test]
140170
fn div() {
141171
assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));

src/test/ui/consts/duration-consts-2.rs

+26-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(const_panic)]
44
#![feature(duration_consts_2)]
55
#![feature(div_duration)]
6+
#![feature(duration_saturating_ops)]
67

78
use std::time::Duration;
89

@@ -15,29 +16,29 @@ fn duration() {
1516

1617
const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
1718

18-
const MAX_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
19-
assert_eq!(MAX_ADD_ZERO, Some(MAX));
19+
const MAX_CHECKED_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
20+
assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX));
2021

21-
const MAX_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
22-
assert_eq!(MAX_ADD_ONE, None);
22+
const MAX_CHECKED_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
23+
assert_eq!(MAX_CHECKED_ADD_ONE, None);
2324

24-
const ONE_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
25-
assert_eq!(ONE_SUB_ONE, Some(ZERO));
25+
const ONE_CHECKED_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
26+
assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO));
2627

27-
const ZERO_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
28-
assert_eq!(ZERO_SUB_ONE, None);
28+
const ZERO_CHECKED_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
29+
assert_eq!(ZERO_CHECKED_SUB_ONE, None);
2930

30-
const ONE_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
31-
assert_eq!(ONE_MUL_ONE, Some(ONE));
31+
const ONE_CHECKED_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
32+
assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE));
3233

33-
const MAX_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
34-
assert_eq!(MAX_MUL_TWO, None);
34+
const MAX_CHECKED_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
35+
assert_eq!(MAX_CHECKED_MUL_TWO, None);
3536

36-
const ONE_DIV_ONE : Option<Duration> = ONE.checked_div(1);
37-
assert_eq!(ONE_DIV_ONE, Some(ONE));
37+
const ONE_CHECKED_DIV_ONE : Option<Duration> = ONE.checked_div(1);
38+
assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE));
3839

39-
const ONE_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
40-
assert_eq!(ONE_DIV_ZERO, None);
40+
const ONE_CHECKED_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
41+
assert_eq!(ONE_CHECKED_DIV_ZERO, None);
4142

4243
const MAX_AS_F32 : f32 = MAX.as_secs_f32();
4344
assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32);
@@ -50,6 +51,15 @@ fn duration() {
5051

5152
const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE);
5253
assert_eq!(ONE_AS_F64, 1.0_f64);
54+
55+
const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE);
56+
assert_eq!(MAX_SATURATING_ADD_ONE, MAX);
57+
58+
const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE);
59+
assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO);
60+
61+
const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2);
62+
assert_eq!(MAX_SATURATING_MUL_TWO, MAX);
5363
}
5464

5565
fn main() {

0 commit comments

Comments
 (0)