-
Notifications
You must be signed in to change notification settings - Fork 185
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
Fine-grained error types #4638
Fine-grained error types #4638
Conversation
70b6ca8
to
d769844
Compare
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 reopens a previous discussion so it needs more discussion
Discuss with: |
This PR largely undoes #2649 The style of returning a wrapped DataError was an explicit choice in #2639 Also some discussion that included @robertbastian in #153 (comment) In general I am in favor of returning smaller error enums in functions that have very specifically documented invariants. Where I differ is in returning a wrapped vs unwrapped error. Returning a non-exhaustive error enum wrapping other variants is preferable to me because:
|
#2639 doesn't really contain any discussion, not even with the people that were linked (I was not). In #153 we decided this for 1.0, but I'm now convinced this was the wrong decision, which is why I want to reopen this for 2.0.
Let's use Java's exceptions as a comparison here. There are checked and unchecked exceptions, checked exceptions are declared on the API and need to be caught by the user, whereas unchecked exceptions are not declared on the API and the user doesn't need to catch them (but can). Yes, using a non-exhaustive error enum gives us more flexibility (like throwing unchecked exceptions in Java gives you more flexibility), but it comes at the cost of users having to pass around unknown future error variants everywhere. This either leads to them being unwrapped somewhere, or users having to include fallback code for situations that do not happen.
And this should totally be a breaking change. Users should explicitly handle these errors, not bubble them through and ignore/unwrap them somewhere. Also, such a change would probably require a bigger API redesign, such as making things generic in the allocator used. |
Also, in #153 I did say
We failed to do that. In datetime we have |
Proposal:
Note about 2b: We should always use fine-grained custom/wrapped errors for this. If such a case is introduced by CLDR/etc, it is unlikely that users would be expecting such a change and may already be Note about 3a: Agreed: @Manishearth @robertbastian @sffc |
For things with options, one solution we can have is requiring validation if the options ever gain invariants. We're not making the decision for now. Either split options/validatedoptions
Or add private fields: #[non_exhaustive]
struct Options {
pub plural_category: PC,
weird_thing: u8,
}
Options::new_with_weird(pc, u8) -> Result<Self>
let foo = Options::new();
foo.plural_category = PC::Cardinal;
foo = foo.with_weird(23).expect("oops"); |
Part of #4336 Per policy in #4638 (comment)
8db53a3
to
dfa4886
Compare
7962cc7
to
84b6538
Compare
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.
LG except for one issue, one nit, and datetime, which I'll let Shane review (since he's going to be deleting large chunks of it anyway)
provider/core/src/error.rs
Outdated
@@ -72,6 +72,9 @@ pub enum DataErrorKind { | |||
/// means that a required Cargo feature was not enabled | |||
#[displaydoc("Unavailable buffer format: {0:?} (does icu_provider need to be compiled with an additional Cargo feature?)")] | |||
UnavailableBufferFormat(BufferFormat), | |||
|
|||
/// The values for two [`DataKey`]s are not consistent. |
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.
nit: please expand on this, on what "not consistent" can mean here
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 want to leave this kind of vague. Do you have a suggestion for wording?
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.
"The values for two datamarkers that deal with related data are not consistent"
perhaps have an example
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.
icu_datetime changes are small and seem ok
self.datetime | ||
.week_of_month(w) | ||
.map_err(DateTimeWriteError::MissingInputField) |
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.
Question/Suggestion: This pattern seems a little strange; why not just make week_of_month
return the DateTimeWriteError
. It's not clear reading the code that week_of_month
returns a &'static str
that is subsequently wrapped.
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 general I try to create errors as late as possible. If week_of_month
returned DateTimeWriteError
, you'd have to dive into the code to figure out which variants happen if you ever wanted to use the method in a different context.
week_of( | ||
self, | ||
day_of_year_info.days_in_prev_year, | ||
day_of_year_info.days_in_year, | ||
day_of_year_info.day_of_year, | ||
iso_weekday, | ||
) | ||
.unwrap_or_else(|_| { |
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.
Nit: use the specific error type like RangeError
instead of _
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.
why?
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 looked at "my" components in more detail and spot-checked the rest.
This is my vision for errors in ICU4X 2.0. Instead of having one catch-call sum type per crate, we should be more exact about which methods can return which errors. Let's look at
icu::calendar::Error
for example: this is returned by bothChinese::new_unstable
, andDate::try_new_gregorian_date
, meaning the user has to handle::Parse
,::Overflow
, etc. forChinese::new_unstable
, as well as::Data
forDate::try_new_gregorian_date
(of course those will never happen).We're trying to be very explicit about data in ICU4X, yet
DataError
s appear everywhere because they are a variant in the catch-all error types. So the idea is:DataError
. If using compiled data, the client can be reasonably sure that this error is impossible::UnsupportedField
(I think this is weird)DateTimeError::Plurals
et alicu4x/components/casemap/src/titlecase.rs
Lines 237 to 242 in 59a8e63
DataError::custom
. This lets us associate a data key with the error (i.e. https://github.com/unicode-org/icu4x/pull/4638/files#diff-39322bc0d217068e658c354db35fa09d51dea5c65707b3f28a129bb261e623b9), and compiled data does not have to care about these. The litmus test for what should be aDataError
should be: does this not happen for (default) compiled data?DataError
s, as the data is already loaded (this is ICU4X's model)icu::properties::sets::load_for_ecma262_unstable
: this currently returns either aDataError
or an invalid property error. We can split this into two methods, one that parses the&str
into a property, and one that loads the data for the property.I've mocked up this change for most crates in this PR. Often, the only error variant was
DataError
, in which case this change was trivial, but sometimes there were other variants that required introducing custom error types. Datetime and calendar are not done, as these crates have huge error types.