-
Notifications
You must be signed in to change notification settings - Fork 533
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
Fallable constructors for less panics (#815) #996
Conversation
- Creates a global Error enum - Breaks backwards compatiblility mainly because of promoting fallable functions (chronotope#263) - Some tests still fall - Not all doctests are fixed - to_naive_datetime_with_offset function is broken and needs fixing - serde related stuff is not checked properly This is a rebase of chronotope#817 onto the 0.5 main branch. Main differences: - Unify three different error structs - Removed ErrorKind - Adapted a lot of unit tests - Removed some commits from presumably unrelated branches (chronotope#829) or mainlined commits (chronotope#271) Co-authored-by: John-John Tedro <udoprog@tedro.se>
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.
Overall, I like this PR because it carries Error information forward to the user. The user can decide what to do with it. The user might print the error to the console so the end-user will have a hint about what went wrong.
#[inline] | ||
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> { | ||
NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time)) | ||
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> Result<DateTime<Tz>, Error> { |
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.
IIUC, it looks like the recently added API fn and_hms_opt
are being removed, and the "old" API like fn and_hms
(previously marked #[deprecated(...)]
) is being resurrected.
I'm afraid this will be too much churn for users. They will have switched from fn and_hms
to fn and_hms_opt
. With this PR, they must now switch back to fn and_hms
.
My recommendation is to:
- leave
fn and_hms_opt
in-place, so to not annoy current users of that API. - Then rewrite
fn and_hms
with the changes proposed here (returningResult<DateTime<...>, Error>
). - The
fn and_hms_opt
should wrap a call tofn and_hms
.
Of course, this should apply to all similar interfaces.
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.
Thanks for your input! I can not give specific reasoning for the case of fn and_hms
and fn and_hms_opt
, but in general I try to follow this pattern:
Return Result<T,E>
for fallible functions and return T
for "safe" functions, with "safe" being explicitly mentioned/required by the documentation or const
context.
Prefixing all fallible functions with try_
was also suggested in #815, but this seems excessive when applied consequently. Once the error-propagation is done, the backwards-compability may be more in focus. This targets v0.5 and will introduce breaking changes.
The mess with fn and_hms
might also be a byproduct of the rebase, which I am still working on and might solve itself later in the progress. The functions related to with_ymd_and_hms
also have a pending cleanup.
Right now I'm stuck on Timezone::offset_from_utc_date()
and Timezone::offset_from_utc_datetime()
. Both are described as "cannot fail", although they should. Neither are deprecated.
TimeZone::from_utc_date()
is deprecated and may panic/fail. TimeZone::from_utc_datetime()
is suggested instead and may panic/fail.
It is hard to judge what behavior is intended and sometimes I have to guess or wait for feedback.
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.
This targets v0.5 and will introduce breaking changes.
I guess it's up to @djc and @esheppa how many breaking changes they want in 0.5 release. My concern is overwhelming users with a seeming "back and forth" approach to and_hms
and and_hms_opt
. (use and_hms
! don't use and_hms
, switch to and_hms_opt
! use and_hms
because and_hms_opt
was removed!)
The mess with
fn and_hms
might also be a byproduct of the rebase
Sorry, I don't know what you mean.
Right now I'm stuck on
Timezone::offset_from_utc_date()
andTimezone::offset_from_utc_datetime()
. Both are described as "cannot fail", although they should. Neither are deprecated.
Good point. Seems like trying to be consistent about "cannot fail" and "should fail" might be a bit of work.
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.
My concern is overwhelming users with a seeming "back and forth" approach to
and_hms
andand_hms_opt
.
I picked up your proposal and added the wrapper for backwards compatibility. This could be applied in other cases as well. However many function had a fingerprint change without receiving a new function name (returning Result<T,E>
instead of just T
). This might lead to additional confusion ...
/// Wraps `and_hms` for backwards compability.
#[deprecated(since = "0.5.0", note = "Use and_hms instead")]
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Result<DateTime<Tz>, Error> {
self.and_hms(hour, min, sec)
}
The mess with
fn and_hms
might also be a byproduct of the rebaseSorry, I don't know what you mean.
The main trunk code base has diverged quite a lot since the original patch in #817. For many conflicts I chose the branch version and therefor dropped changes from the trunk. A few functions have been deprecated in the main trunk while they were still heavily used in the branch (e.g. Utc.ymd()
). I am not sure if and_hms_opt
has already been present in the old branch.
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.
My concern is overwhelming users with a seeming "back and forth" approach to
and_hms
andand_hms_opt
.
I picked up your proposal and added the wrapper for backwards compatibility. This could be applied in other cases as well. However many function had a fingerprint change without receiving a new function name (returning
Result<T,E>
instead of justT
). This might lead to additional confusion .../// Wraps `and_hms` for backwards compability. #[deprecated(since = "0.5.0", note = "Use and_hms instead")] pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Result<DateTime<Tz>, Error> { self.and_hms(hour, min, sec) }
Notice that fn and_hms_opt
(and similar functions) return Option<T>
.
https://github.com/chronotope/chrono/blob/v0.4.23/src/date.rs#L109-L111
Whereas in your proposed change, which you provided a sample of, it is returning Result<T>
. That would still require users to change every call to fn and_hms_opt
(and similar functions). So not very "backwards compatible". The fn and_hms_opt
(and similar functions) should continue to return Option<T>
.
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.
Whoopsie, changed in 33c0647.
I know it's bad to have serde_json as package dependency, but I found no way around it. Many serde tests depend on serde_json and can throw an error.
I would need help with the last two build errors:
Apart from that, it looks somewhat reviewable. Input is especially welcome on the following topics: The
Lastly in many instances those functions would have been helpful. I would gladly add them:
|
@@ -20,6 +20,7 @@ default = ["clock", "std", "wasmbind"] | |||
alloc = [] | |||
libc = [] | |||
std = [] | |||
serde = ["dep:serde", "dep:serde_json"] |
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.
This will be causing the MSRV failure (one of the two CI failures) due to:
Note: The dep: syntax is only available starting with Rust 1.60. Previous versions can only use the implicit feature name.
There are suggestions that we will probably go higher than the current 1.48
(see: #995), but it may not go as high as 1.60
(put another way, we probably can't justify moving to 1.60
just to get this dep:{crate}
syntax)
@Zomtir Impressive work! I am interested in the direction of this PR. Maybe I can help move it forwards a bit... Error enum in this PRYou currently define an pub enum Error {
InvalidDate,
InvalidTime,
InvalidDateTime,
InvalidTimeZone,
AmbiguousDate,
MissingDate,
SystemTimeBeforeEpoch,
ParsingOutOfRange,
ParsingImpossible,
ParsingNotEnough,
ParsingInvalid,
ParsingTooShort,
ParsingTooLong,
ParsingBadFormat,
DateTime(&'static str),
FindLocalTimeType(&'static str),
LocalTimeType(&'static str),
InvalidSlice(&'static str),
InvalidTzFile(&'static str),
InvalidTzString(&'static str),
Io(std::io::ErrorKind),
OutOfRange(&'static str),
ParseInt(core::num::ParseIntError),
ProjectDateTime(&'static str),
SystemTime,
TimeZone(&'static str),
TransitionRule(&'static str),
UnsupportedTzFile(&'static str),
UnsupportedTzString(&'static str),
Utf8(core::str::Utf8Error),
TryFromIntError,
FromUtf8Error,
UnexpectedEOF,
InvalidData,
DurationExceedsTimestamp,
DurationExceedsLimit,
TimestampExceedsLimit,
SerializationError,
} I don't think anyone is served by having this many variants.
A more limited error enumpub enum ChronoError {
InvalidParameter, // example: hour == 25
Overflow, // example: year = 500_000
DateDoesNotExist, // example: 31 February
DurationExceedsTimestamp, // Can `duration_round` not be made smarter to never fail?
LocalTzLoadingError, // Catchall for all errors caused by the OS in `offset::local`
InconsistentDate, // example: parsing Sunday 2023-04-21 (should be Friday)
ParsingError(ParsingError), // collect all other reasons for failing to parse something in a nested enum.
/* maybe more ... */
} Adding useful information for dealing with an errorIt would really depend on the method that causes an error, but maybe some enum variants should carry helpful information. Parsing errorsIn my opinion parsing is such a specific operation, it should keep its own error type. On the other hand are there some overlaps:
I propose to add it as a nested error type in a Local timezone errorsNot sure how deep to go here. Errors mapped from external traitsI am not sure if these errors should even be mapped to another type: TryFromIntError,
FromUtf8Error,
SerializationError, // serde Splitting up this massive PRIs there really no way to split up this PR, and make it reviewable? |
@Zomtir You and @udoprog in #817 put a lot of work into this. I propose to close this PR for now. Speaking personally I would like to spend a couple more months to fix backwards-compatible issues in the 0.4.x branch before working on changing the API in 0.5 to return |
@pitdicker Yes I agree for the most part. This pull request should definitely stay closed. I could squash, rebase and fix the serde issues, but it is massive pull request built on at least two assumptions:
It would be nice to have some initial groundwork/spark by some central contributor and go from there. I have less spare time at the moment so I can make no promises. This PR was mostly a vacation project over a week. I would like to submit one PR to fix a few macros in the unit tests. In at least one case there were very nasty to work with and this could be made pleasant. A second PR could be just a mostly empty |
👍
Shall I cc you when there is a start? |
This is an updated version of fallable constructors. Unfortunately it's quite impossible to split this up into smaller chunks, as mentioned in the original pull request. There are still quite some issues, but would be glad to have some feedback.
This is a rebase of #817 onto the 0.5 main branch. Main differences: