Skip to content

Commit

Permalink
Rollup merge of #102271 - lopopolo:lopopolo/stabilize-duration-try-fr…
Browse files Browse the repository at this point in the history
…om-secs-float, r=dtolnay

Stabilize `duration_checked_float`

## Stabilization Report

This stabilization report is for a stabilization of `duration_checked_float`, tracking issue: rust-lang/rust#83400.

### Implementation History

- rust-lang/rust#82179
- rust-lang/rust#90247
- rust-lang/rust#96051
- Changed error type to `FromFloatSecsError` in rust-lang/rust#90247
- rust-lang/rust#96051 changes the rounding mode to round-to-nearest instead of truncate.

## API Summary

This stabilization report proposes the following API to be stabilized in `core`, along with their re-exports in `std`:

```rust
// core::time

impl Duration {
    pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, TryFromFloatSecsError>;
    pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, TryFromFloatSecsError>;
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TryFromFloatSecsError { ... }

impl core::fmt::Display for TryFromFloatSecsError { ... }
impl core::error::Error for TryFromFloatSecsError { ... }
```

These functions are made const unstable under `duration_consts_float`, tracking issue #72440.

There is an open question in the tracking issue around what the error type should be called which I was hoping to resolve in the context of an FCP.

In this stabilization PR, I have altered the name of the error type to `TryFromFloatSecsError`. In my opinion, the error type shares the name of the method (adjusted to accommodate both types of floats), which is consistent with other error types in `core`, `alloc` and `std` like `TryReserveError` and `TryFromIntError`.

## Experience Report

Code such as this is ready to be converted to a checked API to ensure it is panic free:

```rust
impl Time {
    pub fn checked_add_f64(&self, seconds: f64) -> Result<Self, TimeError> {
        // Fail safely during `f64` conversion to duration
        if seconds.is_nan() || seconds.is_infinite() {
            return Err(TzOutOfRangeError::new().into());
        }

        if seconds.is_sign_positive() {
            self.checked_add(Duration::from_secs_f64(seconds))
        } else {
            self.checked_sub(Duration::from_secs_f64(-seconds))
        }
    }
}
```

See: artichoke/artichoke#2194.

`@rustbot` label +T-libs-api -T-libs

cc `@mbartlett21`
  • Loading branch information
Yuki Okushi committed Oct 24, 2022
2 parents 45888a5 + 84c2a1a commit bc9b441
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 26 deletions.
4 changes: 2 additions & 2 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,8 @@ impl Error for crate::char::ParseCharError {
}
}

#[unstable(feature = "duration_checked_float", issue = "83400")]
impl Error for crate::time::FromFloatSecsError {}
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
impl Error for crate::time::TryFromFloatSecsError {}

#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
impl Error for crate::ffi::FromBytesWithNulError {
Expand Down
37 changes: 17 additions & 20 deletions core/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1225,41 +1225,40 @@ impl fmt::Debug for Duration {
/// # Example
///
/// ```
/// #![feature(duration_checked_float)]
/// use std::time::Duration;
///
/// if let Err(e) = Duration::try_from_secs_f32(-1.0) {
/// println!("Failed conversion to Duration: {e}");
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[unstable(feature = "duration_checked_float", issue = "83400")]
pub struct FromFloatSecsError {
kind: FromFloatSecsErrorKind,
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
pub struct TryFromFloatSecsError {
kind: TryFromFloatSecsErrorKind,
}

impl FromFloatSecsError {
impl TryFromFloatSecsError {
const fn description(&self) -> &'static str {
match self.kind {
FromFloatSecsErrorKind::Negative => {
TryFromFloatSecsErrorKind::Negative => {
"can not convert float seconds to Duration: value is negative"
}
FromFloatSecsErrorKind::OverflowOrNan => {
TryFromFloatSecsErrorKind::OverflowOrNan => {
"can not convert float seconds to Duration: value is either too big or NaN"
}
}
}
}

#[unstable(feature = "duration_checked_float", issue = "83400")]
impl fmt::Display for FromFloatSecsError {
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
impl fmt::Display for TryFromFloatSecsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.description().fmt(f)
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum FromFloatSecsErrorKind {
enum TryFromFloatSecsErrorKind {
// Value is negative.
Negative,
// Value is either too big to be represented as `Duration` or `NaN`.
Expand All @@ -1280,7 +1279,7 @@ macro_rules! try_from_secs {
const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1;

if $secs < 0.0 {
return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::Negative });
return Err(TryFromFloatSecsError { kind: TryFromFloatSecsErrorKind::Negative });
}

let bits = $secs.to_bits();
Expand Down Expand Up @@ -1339,7 +1338,7 @@ macro_rules! try_from_secs {
let secs = u64::from(mant) << (exp - $mant_bits);
(secs, 0)
} else {
return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan });
return Err(TryFromFloatSecsError { kind: TryFromFloatSecsErrorKind::OverflowOrNan });
};

Ok(Duration::new(secs, nanos))
Expand All @@ -1355,8 +1354,6 @@ impl Duration {
///
/// # Examples
/// ```
/// #![feature(duration_checked_float)]
///
/// use std::time::Duration;
///
/// let res = Duration::try_from_secs_f32(0.0);
Expand Down Expand Up @@ -1404,9 +1401,10 @@ impl Duration {
/// let res = Duration::try_from_secs_f32(val);
/// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
/// ```
#[unstable(feature = "duration_checked_float", issue = "83400")]
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
#[inline]
pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, FromFloatSecsError> {
pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, TryFromFloatSecsError> {
try_from_secs!(
secs = secs,
mantissa_bits = 23,
Expand All @@ -1425,8 +1423,6 @@ impl Duration {
///
/// # Examples
/// ```
/// #![feature(duration_checked_float)]
///
/// use std::time::Duration;
///
/// let res = Duration::try_from_secs_f64(0.0);
Expand Down Expand Up @@ -1482,9 +1478,10 @@ impl Duration {
/// let res = Duration::try_from_secs_f64(val);
/// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
/// ```
#[unstable(feature = "duration_checked_float", issue = "83400")]
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
#[inline]
pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, FromFloatSecsError> {
pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, TryFromFloatSecsError> {
try_from_secs!(
secs = secs,
mantissa_bits = 52,
Expand Down
1 change: 0 additions & 1 deletion core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
#![feature(provide_any)]
#![feature(utf8_chunks)]
#![feature(is_ascii_octdigit)]
#![feature(duration_checked_float)]
#![deny(unsafe_op_in_unsafe_fn)]

extern crate test;
Expand Down
1 change: 0 additions & 1 deletion std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,6 @@
#![feature(core_intrinsics)]
#![feature(cstr_from_bytes_until_nul)]
#![feature(cstr_internals)]
#![feature(duration_checked_float)]
#![feature(duration_constants)]
#![feature(error_generic_member_access)]
#![feature(error_in_core)]
Expand Down
4 changes: 2 additions & 2 deletions std/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ use crate::sys_common::{FromInner, IntoInner};
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;

#[unstable(feature = "duration_checked_float", issue = "83400")]
pub use core::time::FromFloatSecsError;
#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
pub use core::time::TryFromFloatSecsError;

/// A measurement of a monotonically nondecreasing clock.
/// Opaque and useful only with [`Duration`].
Expand Down

0 comments on commit bc9b441

Please sign in to comment.