Summary

This RFC adds the checked_* methods already known from primitives like usize to Duration.

Motivation

Generally this helps when subtracting Durations which can be the case quite often.

One abstract example would be executing a specific piece of code repeatedly after a constant amount of time.

Specific examples would be a network service or a rendering process emitting a constant amount of frames per second.

Example code would be as follows:

// This function is called repeatedly fn render() { // 10ms delay results in 100 frames per second let wait_time = Duration::from_millis(10); // `Instant` for elapsed time let start = Instant::now(); // execute code here render_and_output_frame(); // there are no negative `Duration`s so this does nothing if the elapsed // time is longer than the defined `wait_time` start.elapsed().checked_sub(wait_time).and_then(std::thread::sleep); }

Of course it is also suitable to not introduce panic!()s when adding Durations.

Detailed design

The detailed design would be exactly as the current sub() method, just returning an Option<Duration> and passing possible None values from the underlying primitive types:

impl Duration { fn checked_sub(self, rhs: Duration) -> Option<Duration> { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { let nanos = if self.nanos >= rhs.nanos { self.nanos - rhs.nanos } else { if let Some(secs) = secs.checked_sub(1) { self.nanos + NANOS_PER_SEC - rhs.nanos } else { return None; } }; debug_assert!(nanos < NANOS_PER_SEC); Some(Duration { secs: secs, nanos: nanos }) } else { None } } }

The same accounts for all other added methods, namely:

  • checked_add()
  • checked_sub()
  • checked_mul()
  • checked_div()

Drawbacks

None.

Alternatives

The alternatives are simply not doing this and forcing the programmer to code the check on their behalf. This is not what you want.

Unresolved questions

None.