Skip to content

Commit

Permalink
Rollup merge of #78216 - workingjubilee:duration-zero, r=m-ou-se
Browse files Browse the repository at this point in the history
Duration::zero() -> Duration::ZERO

In review for #72790, whether or not a constant or a function should be favored for `#![feature(duration_zero)]` was seen as an open question. In #73544 (comment) an invitation was opened to either stabilize the methods or propose a switch to the constant value, supplemented with reasoning. Followup comments suggested community preference leans towards the const ZERO, which would be reason enough.

ZERO also "makes sense" beside existing associated consts for Duration. It is ever so slightly awkward to have a series of constants specifying 1 of various units but leave 0 as a method, especially when they are side-by-side in code. It seems unintuitive for the one non-dynamic value (that isn't from Default) to be not-a-const, which could hurt discoverability of the associated constants overall. Elsewhere in `std`, methods for obtaining a constant value were even deprecated, as seen with [std::u32::min_value](https://doc.rust-lang.org/std/primitive.u32.html#method.min_value).

Most importantly, ZERO costs less to use. A match supports a const pattern, but const fn can only be used if evaluated through a const context such as an inline `const { const_fn() }` or a `const NAME: T = const_fn()` declaration elsewhere. Likewise, while #73544 (comment) notes `Duration::zero()` can optimize to a constant value, "can" is not "will". Only const contexts have a strong promise of such. Even without that in mind, the comment in question still leans in favor of the constant for simplicity. As it costs less for a developer to use, may cost less to optimize, and seems to have more of a community consensus for it, the associated const seems best.

r? ```@LukasKalbertodt```
  • Loading branch information
jonas-schievink committed Nov 11, 2020
2 parents 7afc517 + 82f3a23 commit 62f0a78
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 95 deletions.
40 changes: 12 additions & 28 deletions library/core/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,20 @@ impl Duration {
#[unstable(feature = "duration_constants", issue = "57391")]
pub const NANOSECOND: Duration = Duration::from_nanos(1);

/// The minimum duration.
/// A duration of zero time.
///
/// # Examples
///
/// ```
/// #![feature(duration_constants)]
/// #![feature(duration_zero)]
/// use std::time::Duration;
///
/// assert_eq!(Duration::MIN, Duration::new(0, 0));
/// let duration = Duration::ZERO;
/// assert!(duration.is_zero());
/// assert_eq!(duration.as_nanos(), 0);
/// ```
#[unstable(feature = "duration_constants", issue = "57391")]
pub const MIN: Duration = Duration::from_nanos(0);
#[unstable(feature = "duration_zero", issue = "73544")]
pub const ZERO: Duration = Duration::from_nanos(0);

/// The maximum duration.
///
Expand Down Expand Up @@ -166,24 +168,6 @@ impl Duration {
Duration { secs, nanos }
}

/// Creates a new `Duration` that spans no time.
///
/// # Examples
///
/// ```
/// #![feature(duration_zero)]
/// use std::time::Duration;
///
/// let duration = Duration::zero();
/// assert!(duration.is_zero());
/// assert_eq!(duration.as_nanos(), 0);
/// ```
#[unstable(feature = "duration_zero", issue = "73544")]
#[inline]
pub const fn zero() -> Duration {
Duration { secs: 0, nanos: 0 }
}

/// Creates a new `Duration` from the specified number of whole seconds.
///
/// # Examples
Expand Down Expand Up @@ -277,7 +261,7 @@ impl Duration {
/// #![feature(duration_zero)]
/// use std::time::Duration;
///
/// assert!(Duration::zero().is_zero());
/// assert!(Duration::ZERO.is_zero());
/// assert!(Duration::new(0, 0).is_zero());
/// assert!(Duration::from_nanos(0).is_zero());
/// assert!(Duration::from_secs(0).is_zero());
Expand Down Expand Up @@ -536,26 +520,26 @@ impl Duration {
}
}

/// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`]
/// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::ZERO`]
/// if the result would be negative or if overflow occurred.
///
/// # Examples
///
/// ```
/// #![feature(duration_saturating_ops)]
/// #![feature(duration_constants)]
/// #![feature(duration_zero)]
/// 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);
/// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::ZERO);
/// ```
#[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,
None => Duration::ZERO,
}
}

Expand Down
97 changes: 46 additions & 51 deletions library/core/tests/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,24 @@ fn sub() {

#[test]
fn checked_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.checked_sub(zero), Some(Duration::new(0, 1)));
assert_eq!(one_sec.checked_sub(one_nano), Some(Duration::new(0, 999_999_999)));
assert_eq!(zero.checked_sub(one_nano), None);
assert_eq!(zero.checked_sub(one_sec), None);
assert_eq!(Duration::NANOSECOND.checked_sub(Duration::ZERO), Some(Duration::NANOSECOND));
assert_eq!(
Duration::SECOND.checked_sub(Duration::NANOSECOND),
Some(Duration::new(0, 999_999_999))
);
assert_eq!(Duration::ZERO.checked_sub(Duration::NANOSECOND), None);
assert_eq!(Duration::ZERO.checked_sub(Duration::SECOND), 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);
assert_eq!(Duration::NANOSECOND.saturating_sub(Duration::ZERO), Duration::NANOSECOND);
assert_eq!(
Duration::SECOND.saturating_sub(Duration::NANOSECOND),
Duration::new(0, 999_999_999)
);
assert_eq!(Duration::ZERO.saturating_sub(Duration::NANOSECOND), Duration::ZERO);
assert_eq!(Duration::ZERO.saturating_sub(Duration::SECOND), Duration::ZERO);
}

#[test]
Expand Down Expand Up @@ -337,87 +337,82 @@ fn duration_const() {
const SUB_SEC_NANOS: u32 = DURATION.subsec_nanos();
assert_eq!(SUB_SEC_NANOS, 123_456_789);

const ZERO: Duration = Duration::zero();
assert_eq!(ZERO, Duration::new(0, 0));

const IS_ZERO: bool = ZERO.is_zero();
const IS_ZERO: bool = Duration::ZERO.is_zero();
assert!(IS_ZERO);

const ONE: Duration = Duration::new(1, 0);

const SECONDS: u64 = ONE.as_secs();
const SECONDS: u64 = Duration::SECOND.as_secs();
assert_eq!(SECONDS, 1);

const FROM_SECONDS: Duration = Duration::from_secs(1);
assert_eq!(FROM_SECONDS, ONE);
assert_eq!(FROM_SECONDS, Duration::SECOND);

const SECONDS_F32: f32 = ONE.as_secs_f32();
const SECONDS_F32: f32 = Duration::SECOND.as_secs_f32();
assert_eq!(SECONDS_F32, 1.0);

const FROM_SECONDS_F32: Duration = Duration::from_secs_f32(1.0);
assert_eq!(FROM_SECONDS_F32, ONE);
assert_eq!(FROM_SECONDS_F32, Duration::SECOND);

const SECONDS_F64: f64 = ONE.as_secs_f64();
const SECONDS_F64: f64 = Duration::SECOND.as_secs_f64();
assert_eq!(SECONDS_F64, 1.0);

const FROM_SECONDS_F64: Duration = Duration::from_secs_f64(1.0);
assert_eq!(FROM_SECONDS_F64, ONE);
assert_eq!(FROM_SECONDS_F64, Duration::SECOND);

const MILLIS: u128 = ONE.as_millis();
const MILLIS: u128 = Duration::SECOND.as_millis();
assert_eq!(MILLIS, 1_000);

const FROM_MILLIS: Duration = Duration::from_millis(1_000);
assert_eq!(FROM_MILLIS, ONE);
assert_eq!(FROM_MILLIS, Duration::SECOND);

const MICROS: u128 = ONE.as_micros();
const MICROS: u128 = Duration::SECOND.as_micros();
assert_eq!(MICROS, 1_000_000);

const FROM_MICROS: Duration = Duration::from_micros(1_000_000);
assert_eq!(FROM_MICROS, ONE);
assert_eq!(FROM_MICROS, Duration::SECOND);

const NANOS: u128 = ONE.as_nanos();
const NANOS: u128 = Duration::SECOND.as_nanos();
assert_eq!(NANOS, 1_000_000_000);

const FROM_NANOS: Duration = Duration::from_nanos(1_000_000_000);
assert_eq!(FROM_NANOS, ONE);
assert_eq!(FROM_NANOS, Duration::SECOND);

const MAX: Duration = Duration::new(u64::MAX, 999_999_999);

const CHECKED_ADD: Option<Duration> = MAX.checked_add(ONE);
const CHECKED_ADD: Option<Duration> = MAX.checked_add(Duration::SECOND);
assert_eq!(CHECKED_ADD, None);

const CHECKED_SUB: Option<Duration> = ZERO.checked_sub(ONE);
const CHECKED_SUB: Option<Duration> = Duration::ZERO.checked_sub(Duration::SECOND);
assert_eq!(CHECKED_SUB, None);

const CHECKED_MUL: Option<Duration> = ONE.checked_mul(1);
assert_eq!(CHECKED_MUL, Some(ONE));
const CHECKED_MUL: Option<Duration> = Duration::SECOND.checked_mul(1);
assert_eq!(CHECKED_MUL, Some(Duration::SECOND));

const MUL_F32: Duration = ONE.mul_f32(1.0);
assert_eq!(MUL_F32, ONE);
const MUL_F32: Duration = Duration::SECOND.mul_f32(1.0);
assert_eq!(MUL_F32, Duration::SECOND);

const MUL_F64: Duration = ONE.mul_f64(1.0);
assert_eq!(MUL_F64, ONE);
const MUL_F64: Duration = Duration::SECOND.mul_f64(1.0);
assert_eq!(MUL_F64, Duration::SECOND);

const CHECKED_DIV: Option<Duration> = ONE.checked_div(1);
assert_eq!(CHECKED_DIV, Some(ONE));
const CHECKED_DIV: Option<Duration> = Duration::SECOND.checked_div(1);
assert_eq!(CHECKED_DIV, Some(Duration::SECOND));

const DIV_F32: Duration = ONE.div_f32(1.0);
assert_eq!(DIV_F32, ONE);
const DIV_F32: Duration = Duration::SECOND.div_f32(1.0);
assert_eq!(DIV_F32, Duration::SECOND);

const DIV_F64: Duration = ONE.div_f64(1.0);
assert_eq!(DIV_F64, ONE);
const DIV_F64: Duration = Duration::SECOND.div_f64(1.0);
assert_eq!(DIV_F64, Duration::SECOND);

const DIV_DURATION_F32: f32 = ONE.div_duration_f32(ONE);
const DIV_DURATION_F32: f32 = Duration::SECOND.div_duration_f32(Duration::SECOND);
assert_eq!(DIV_DURATION_F32, 1.0);

const DIV_DURATION_F64: f64 = ONE.div_duration_f64(ONE);
const DIV_DURATION_F64: f64 = Duration::SECOND.div_duration_f64(Duration::SECOND);
assert_eq!(DIV_DURATION_F64, 1.0);

const SATURATING_ADD: Duration = MAX.saturating_add(ONE);
const SATURATING_ADD: Duration = MAX.saturating_add(Duration::SECOND);
assert_eq!(SATURATING_ADD, MAX);

const SATURATING_SUB: Duration = ZERO.saturating_sub(ONE);
assert_eq!(SATURATING_SUB, ZERO);
const SATURATING_SUB: Duration = Duration::ZERO.saturating_sub(Duration::SECOND);
assert_eq!(SATURATING_SUB, Duration::ZERO);

const SATURATING_MUL: Duration = MAX.saturating_mul(2);
assert_eq!(SATURATING_MUL, MAX);
Expand Down
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
#![feature(doc_spotlight)]
#![feature(dropck_eyepatch)]
#![feature(duration_constants)]
#![feature(duration_zero)]
#![feature(exact_size_is_empty)]
#![feature(exhaustive_patterns)]
#![feature(extend_one)]
Expand Down
32 changes: 16 additions & 16 deletions library/std/src/time/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro_rules! assert_almost_eq {
let (a, b) = ($a, $b);
if a != b {
let (a, b) = if a > b { (a, b) } else { (b, a) };
assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b);
assert!(a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b);
}
}};
}
Expand Down Expand Up @@ -34,7 +34,7 @@ fn instant_math() {
assert_almost_eq!(b - dur, a);
assert_almost_eq!(a + dur, b);

let second = Duration::new(1, 0);
let second = Duration::SECOND;
assert_almost_eq!(a - second + second, a);
assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a);

Expand Down Expand Up @@ -65,32 +65,32 @@ fn instant_math_is_associative() {
#[should_panic]
fn instant_duration_since_panic() {
let a = Instant::now();
(a - Duration::new(1, 0)).duration_since(a);
(a - Duration::SECOND).duration_since(a);
}

#[test]
fn instant_checked_duration_since_nopanic() {
let now = Instant::now();
let earlier = now - Duration::new(1, 0);
let later = now + Duration::new(1, 0);
let earlier = now - Duration::SECOND;
let later = now + Duration::SECOND;
assert_eq!(earlier.checked_duration_since(now), None);
assert_eq!(later.checked_duration_since(now), Some(Duration::new(1, 0)));
assert_eq!(now.checked_duration_since(now), Some(Duration::new(0, 0)));
assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND));
assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO));
}

#[test]
fn instant_saturating_duration_since_nopanic() {
let a = Instant::now();
let ret = (a - Duration::new(1, 0)).saturating_duration_since(a);
assert_eq!(ret, Duration::new(0, 0));
let ret = (a - Duration::SECOND).saturating_duration_since(a);
assert_eq!(ret, Duration::ZERO);
}

#[test]
fn system_time_math() {
let a = SystemTime::now();
let b = SystemTime::now();
match b.duration_since(a) {
Ok(dur) if dur == Duration::new(0, 0) => {
Ok(Duration::ZERO) => {
assert_almost_eq!(a, b);
}
Ok(dur) => {
Expand All @@ -106,16 +106,16 @@ fn system_time_math() {
}
}

let second = Duration::new(1, 0);
let second = Duration::SECOND;
assert_almost_eq!(a.duration_since(a - second).unwrap(), second);
assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second);

assert_almost_eq!(a - second + second, a);
assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a);

let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0);
let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND;
let one_second_from_epoch2 =
UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000);
UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500);
assert_eq!(one_second_from_epoch, one_second_from_epoch2);

// checked_add_duration will not panic on overflow
Expand All @@ -141,12 +141,12 @@ fn system_time_elapsed() {
#[test]
fn since_epoch() {
let ts = SystemTime::now();
let a = ts.duration_since(UNIX_EPOCH + Duration::new(1, 0)).unwrap();
let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap();
let b = ts.duration_since(UNIX_EPOCH).unwrap();
assert!(b > a);
assert_eq!(b - a, Duration::new(1, 0));
assert_eq!(b - a, Duration::SECOND);

let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30;
let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30;

// Right now for CI this test is run in an emulator, and apparently the
// aarch64 emulator's sense of time is that we're still living in the
Expand Down

0 comments on commit 62f0a78

Please sign in to comment.