Skip to content

Commit 1e14d39

Browse files
authored
Rollup merge of #82179 - mbartlett21:patch-5, r=joshtriplett
Add functions `Duration::try_from_secs_{f32, f64}` These functions allow constructing a Duration from a floating point value that could be out of range without panicking. Tracking issue: #83400
2 parents 2d2f1a5 + 7803955 commit 1e14d39

File tree

3 files changed

+132
-24
lines changed

3 files changed

+132
-24
lines changed

library/core/src/time.rs

+128-24
Original file line numberDiff line numberDiff line change
@@ -687,21 +687,47 @@ impl Duration {
687687
#[inline]
688688
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
689689
pub const fn from_secs_f64(secs: f64) -> Duration {
690+
match Duration::try_from_secs_f64(secs) {
691+
Ok(v) => v,
692+
Err(e) => crate::panicking::panic(e.description()),
693+
}
694+
}
695+
696+
/// The checked version of [`from_secs_f64`].
697+
///
698+
/// [`from_secs_f64`]: Duration::from_secs_f64
699+
///
700+
/// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`.
701+
///
702+
/// # Examples
703+
/// ```
704+
/// #![feature(duration_checked_float)]
705+
///
706+
/// use std::time::Duration;
707+
///
708+
/// let dur = Duration::try_from_secs_f64(2.7);
709+
/// assert_eq!(dur, Ok(Duration::new(2, 700_000_000)));
710+
///
711+
/// let negative = Duration::try_from_secs_f64(-5.0);
712+
/// assert!(negative.is_err());
713+
/// ```
714+
#[unstable(feature = "duration_checked_float", issue = "83400")]
715+
#[inline]
716+
pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, FromSecsError> {
690717
const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64;
691718
let nanos = secs * (NANOS_PER_SEC as f64);
692719
if !nanos.is_finite() {
693-
panic!("got non-finite value when converting float to duration");
694-
}
695-
if nanos >= MAX_NANOS_F64 {
696-
panic!("overflow when converting float to duration");
697-
}
698-
if nanos < 0.0 {
699-
panic!("underflow when converting float to duration");
700-
}
701-
let nanos = nanos as u128;
702-
Duration {
703-
secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
704-
nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
720+
Err(FromSecsError { kind: FromSecsErrorKind::NonFinite })
721+
} else if nanos >= MAX_NANOS_F64 {
722+
Err(FromSecsError { kind: FromSecsErrorKind::Overflow })
723+
} else if nanos < 0.0 {
724+
Err(FromSecsError { kind: FromSecsErrorKind::Underflow })
725+
} else {
726+
let nanos = nanos as u128;
727+
Ok(Duration {
728+
secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
729+
nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
730+
})
705731
}
706732
}
707733

@@ -722,21 +748,47 @@ impl Duration {
722748
#[inline]
723749
#[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
724750
pub const fn from_secs_f32(secs: f32) -> Duration {
751+
match Duration::try_from_secs_f32(secs) {
752+
Ok(v) => v,
753+
Err(e) => crate::panicking::panic(e.description()),
754+
}
755+
}
756+
757+
/// The checked version of [`from_secs_f32`].
758+
///
759+
/// [`from_secs_f32`]: Duration::from_secs_f32
760+
///
761+
/// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`.
762+
///
763+
/// # Examples
764+
/// ```
765+
/// #![feature(duration_checked_float)]
766+
///
767+
/// use std::time::Duration;
768+
///
769+
/// let dur = Duration::try_from_secs_f32(2.7);
770+
/// assert_eq!(dur, Ok(Duration::new(2, 700_000_000)));
771+
///
772+
/// let negative = Duration::try_from_secs_f32(-5.0);
773+
/// assert!(negative.is_err());
774+
/// ```
775+
#[unstable(feature = "duration_checked_float", issue = "83400")]
776+
#[inline]
777+
pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, FromSecsError> {
725778
const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32;
726779
let nanos = secs * (NANOS_PER_SEC as f32);
727780
if !nanos.is_finite() {
728-
panic!("got non-finite value when converting float to duration");
729-
}
730-
if nanos >= MAX_NANOS_F32 {
731-
panic!("overflow when converting float to duration");
732-
}
733-
if nanos < 0.0 {
734-
panic!("underflow when converting float to duration");
735-
}
736-
let nanos = nanos as u128;
737-
Duration {
738-
secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
739-
nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
781+
Err(FromSecsError { kind: FromSecsErrorKind::NonFinite })
782+
} else if nanos >= MAX_NANOS_F32 {
783+
Err(FromSecsError { kind: FromSecsErrorKind::Overflow })
784+
} else if nanos < 0.0 {
785+
Err(FromSecsError { kind: FromSecsErrorKind::Underflow })
786+
} else {
787+
let nanos = nanos as u128;
788+
Ok(Duration {
789+
secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
790+
nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
791+
})
740792
}
741793
}
742794

@@ -1099,3 +1151,55 @@ impl fmt::Debug for Duration {
10991151
}
11001152
}
11011153
}
1154+
1155+
/// An error which can be returned when converting a floating-point value of seconds
1156+
/// into a [`Duration`].
1157+
///
1158+
/// This error is used as the error type for [`Duration::try_from_secs_f32`] and
1159+
/// [`Duration::try_from_secs_f64`].
1160+
///
1161+
/// # Example
1162+
///
1163+
/// ```
1164+
/// #![feature(duration_checked_float)]
1165+
///
1166+
/// use std::time::Duration;
1167+
///
1168+
/// if let Err(e) = Duration::try_from_secs_f32(-1.0) {
1169+
/// println!("Failed conversion to Duration: {}", e);
1170+
/// }
1171+
/// ```
1172+
#[derive(Debug, Clone, PartialEq, Eq)]
1173+
#[unstable(feature = "duration_checked_float", issue = "83400")]
1174+
pub struct FromSecsError {
1175+
kind: FromSecsErrorKind,
1176+
}
1177+
1178+
impl FromSecsError {
1179+
const fn description(&self) -> &'static str {
1180+
match self.kind {
1181+
FromSecsErrorKind::NonFinite => {
1182+
"got non-finite value when converting float to duration"
1183+
}
1184+
FromSecsErrorKind::Overflow => "overflow when converting float to duration",
1185+
FromSecsErrorKind::Underflow => "underflow when converting float to duration",
1186+
}
1187+
}
1188+
}
1189+
1190+
#[unstable(feature = "duration_checked_float", issue = "83400")]
1191+
impl fmt::Display for FromSecsError {
1192+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1193+
fmt::Display::fmt(self.description(), f)
1194+
}
1195+
}
1196+
1197+
#[derive(Debug, Clone, PartialEq, Eq)]
1198+
enum FromSecsErrorKind {
1199+
// Value is not a finite value (either infinity or NaN).
1200+
NonFinite,
1201+
// Value is too large to store in a `Duration`.
1202+
Overflow,
1203+
// Value is less than `0.0`.
1204+
Underflow,
1205+
}

library/std/src/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,9 @@ impl Error for char::ParseCharError {
597597
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
598598
impl Error for alloc::collections::TryReserveError {}
599599

600+
#[unstable(feature = "duration_checked_float", issue = "83400")]
601+
impl Error for core::time::FromSecsError {}
602+
600603
// Copied from `any.rs`.
601604
impl dyn Error + 'static {
602605
/// Returns `true` if the boxed type is the same as `T`

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@
261261
#![feature(doc_masked)]
262262
#![feature(doc_notable_trait)]
263263
#![feature(dropck_eyepatch)]
264+
#![feature(duration_checked_float)]
264265
#![feature(duration_constants)]
265266
#![feature(edition_panic)]
266267
#![feature(exact_size_is_empty)]

0 commit comments

Comments
 (0)