-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added an implementation of From<Time> for Duration #131
Changes from 5 commits
5bb09d5
258ecb5
7515865
14bcc35
9755822
3e3969f
9caa47b
82d6e99
8493f01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
//! Time (base unit second, s<sup>1</sup>). | ||
|
||
use ::lib::time::Duration; | ||
|
||
quantity! { | ||
/// Time (base unit second, s<sup>1</sup>). | ||
quantity: Time; "time"; | ||
|
@@ -44,3 +46,41 @@ quantity! { | |
@year: 3.1536_E7; "a", "year", "years"; | ||
} | ||
} | ||
|
||
impl<U, V> From<Time<U, V>> for Duration | ||
where | ||
U: ::si::Units<V> + ?Sized, | ||
V: ::num::Num + ::num::AsPrimitive<u64> + ::num::AsPrimitive<u32> + ::Conversion<V> + ::lib::cmp::PartialOrd, | ||
second: ::Conversion<V, T = V::T>, | ||
nanosecond: ::Conversion<V, T = V::T>, | ||
{ | ||
fn from(t: Time<U, V>) -> Duration { | ||
let t = if t < Time::<U, V>::new::<second>(V::zero()) { | ||
Time::<U, V>::new::<second>(V::zero()) - t | ||
} else { | ||
t | ||
}; | ||
let secs = t.get::<second>().as_(); | ||
let nanos = (t % Time::<U, V>::new::<second>(V::one())).get::<nanosecond>().as_(); | ||
Duration::new(secs, nanos) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
storage_types! { | ||
types: Float; | ||
|
||
use ::lib::time::Duration; | ||
use si::quantities::*; | ||
use si::time::second; | ||
|
||
#[test] | ||
fn from() { | ||
let t = Time::new::<second>(-21.5); | ||
let d: Duration = t.into(); | ||
assert_eq!(d.as_secs(), 21); | ||
assert!(499_999_999 <= d.subsec_nanos() && d.subsec_nanos() <= 500_000_001); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason that we can't require this to be exactly 500,000,000? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Precision loss when I subtract t from zero is my current best guess. The way I implemented it originally, by casting the time to f64 and then converting that to a Duration did allow this to be 500,000,000 exactly. Perhaps it's only the f32 test that loses precision? |
||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand why this is here, but do we want to silently convert negative times to (inherently positive) durations? I suppose this might be a matter of "time vs. time interval."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's really a silent conversion, the user has to manually call
into
in a context where the conversion is into an inherently positive type. I guess it's a question of which is least surprising:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that I would prefer panicking, although I’d imagine that others would see it differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really? I just put that option there for completeness, From must not fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the context of that documentation page, "fallible functions" seem to be loosely be "functions that return
Result<T, E>
orOption<T>
:As such, I read that note as simply meaning "
From
does not return an error type." A more general argument for this interpretation is that it's almost nonsensical to say "this code must not panic" (with a couple of notable exceptions) in Safe Rust.Panics signal an irrecoverable error, something that makes no sense and for which there's no sensible alternative route. In my mind, converting a negative time to a
Duration
is such an error, and it makes sense to panic, as this likely signals a logic error. However, panicking from within a library crate is certainly not ideal.I actually might argue, if possible, that we should use
TryFrom
instead ofFrom
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So
TryFrom
vsFrom
is a different issue, there's certainly an argument to be made there. The trick is, doing that sort of error management (while certainly more rigorous) is enough of an ergonomics hit that if that's the directionuom
decides to go I'm going to just continue defining my own conversion functions where and as I need them. I'm not saying you're wrong, you're not,TryFrom
is definitely a more correct fit (even setting aside the absolute value question, NaN is sufficient to justifyTryFrom
), it just feels cumbersome (and the implementation is going to be even more of a nightmare).So with that out of the way, I think your reading of the note is wrong, and here's why. There's absolutely no point saying "
From
does not return an error type", because there is no flexibility infrom
's return type. There's simply no choice what you return. It would be warning us to make sure our code builds, or warning us to not write bugs into our code.If you assume they added (and emphasized) that warning for a reason, there must be more to it than that. It must be more general than "don't do what you can't do". The reason they warn us not to let it fail is because From is integral to Rust's failure handling mechanisms, and it's not at all clear what should happen when failure handling fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... so... I think I just accidentally convinced myself that this PR should be rejected on account of NaN and the infinities. sigh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to beat a dead horse, but I still believe that the meaning for that documentation for
From
is simply "the return value is not an error type." This is especially supported by the fact that you canimpl From<A> for Option<B>
to mimicTryFrom
(and so there is indeed some flexibility in the return type). Regardless, I do think thatTryFrom
is the better fit here. May I ask whyFrom
is necessary for your use case/whyTryFrom
is insufficient?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not that the trait is insufficient on its own (I mean, it's a bit cumbersome to have to do yet more error handling, but at the end of the day that's not that big a deal), it's more that the difficulty of making the change is greater than the amount I care about it being made. I've already sunk 100x more time into this PR than its actually worth to me, and if I were to double that investment I still don't think that's enough to adequately implement TryFrom.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, thanks. I think I might try to make up a
TryFrom
PR, fwiw. Thanks for your efforts on this.