Skip to content

Commit 077d6d2

Browse files
committed
Switch to TryFrom conversions for TimeSpec and Duration
Their underlying types are not congruent and therefor "lossy" conversions with usually unexpected behavior result from it. Let's make this explicit by switching from From conversions to TryFrom. The error types used there are quite spartan. Is there a need for more detailed reporting?
1 parent c7e35cd commit 077d6d2

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

src/sys/time.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,41 @@ impl From<timespec> for TimeSpec {
209209
}
210210
}
211211

212-
impl From<Duration> for TimeSpec {
213-
fn from(duration: Duration) -> Self {
214-
Self::from_duration(duration)
212+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
213+
pub struct TryFromDurationError;
214+
215+
impl TryFrom<Duration> for TimeSpec {
216+
type Error = TryFromDurationError;
217+
218+
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
219+
Self::try_from_duration(duration)
215220
}
216221
}
217222

218-
impl From<TimeSpec> for Duration {
219-
fn from(timespec: TimeSpec) -> Self {
220-
Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
223+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
224+
pub struct TryFromTimeSpecError;
225+
226+
impl TryFrom<TimeSpec> for Duration {
227+
type Error = TryFromTimeSpecError;
228+
229+
fn try_from(timespec: TimeSpec) -> Result<Self, Self::Error> {
230+
let secs = timespec
231+
.0
232+
.tv_sec
233+
.try_into()
234+
.map_err(|_| TryFromTimeSpecError)?;
235+
let nanos = timespec
236+
.0
237+
.tv_nsec
238+
.try_into()
239+
.map_err(|_| TryFromTimeSpecError)?;
240+
241+
// Error out here rather than letting Duration::new panic.
242+
if nanos > Duration::MAX.subsec_nanos() {
243+
return Err(TryFromTimeSpecError);
244+
}
245+
246+
Ok(Duration::new(secs, nanos))
221247
}
222248
}
223249

@@ -365,13 +391,16 @@ impl TimeSpec {
365391
self.0.tv_nsec
366392
}
367393

368-
#[cfg_attr(target_env = "musl", allow(deprecated))]
369-
// https://github.com/rust-lang/libc/issues/1848
370-
pub const fn from_duration(duration: Duration) -> Self {
394+
pub fn try_from_duration(
395+
duration: Duration,
396+
) -> Result<Self, TryFromDurationError> {
371397
let mut ts = zero_init_timespec();
372-
ts.tv_sec = duration.as_secs() as time_t;
373-
ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
374-
TimeSpec(ts)
398+
ts.tv_sec = duration
399+
.as_secs()
400+
.try_into()
401+
.map_err(|_| TryFromDurationError)?;
402+
ts.tv_nsec = duration.subsec_nanos().into();
403+
Ok(TimeSpec(ts))
375404
}
376405

377406
pub const fn from_timespec(timespec: timespec) -> Self {

0 commit comments

Comments
 (0)