-
Notifications
You must be signed in to change notification settings - Fork 213
Introduce throws keyword (like Swift) for marking throwable functions #4321
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
Comments
One of the biggest problems with writing try catch in dart is that is figuring out which specific exceptions can be thrown, (not just if an exception can be thrown). So I think you would need to be able to specify the exception types. int parseIntStrict(String input) throws FormatException {
if (!RegExp(r'^\d+$').hasMatch(input)) {
throw FormatException("Invalid number format");
}
return int.parse(input);
} That way the tooling can suggest: void main() {
try {
final x = parseIntStrict('hello');
} on FormatException catch (e) { // catching specific exception
print('unable to parse x');
}
} Instead of just: void main() {
try {
final x = parseIntStrict('hello');
} catch (e) { // catching any exception
print('unable to parse x');
}
} |
The current recommended way to document exceptions is to have a paragraph in the DartDoc starting with the word "Throws" and linking to the exceptions being thrown. If your function had that, /// ...
/// Throws a [FormatException] if not
/// a sequence of digits. then your users can see what to throw, and tools could choose to pick up on that too. The documented exceptions of a function are those classes which subtype |
@mmcdon20, I believe Swift addresses a similar concern by generalising all functions as non-throwing unless specified A translated Dart-equivalent would roughly look like: int nonThrowable() => 2;
// Equivalent to
int nonThrowable() throws Never => 2;
int throwable() throws => throw 2;
// Equivalent to
int throwable() throws Object => throw 2;
// Self-explanatory
int specificThrowable() throws FormatException => throw FormatException('2'); Though, I'm not sure how much of a breaking change this would be, since all functions/methods would be assumed as non-throwing in the current case. |
@lrhn I believe your suggestion of create those two lint rules |
The problem with If a function you call adds If someone added Any function call can throw, if only due to stack overflow. Dart reifies that as an in-program error object. That also leads us to It's interesting to read the C# approach to this: https://www.artima.com/articles/the-trouble-with-checked-exceptions So maybe go the other way: Add a We can promise that fx |
Another way is to write and use annotations and analyzer plugin: @Throws<FormatException>()
@Throws<int>()
void canThrowExcpetionsAndObjects() {
// pass
} |
@lrhn what if we create something like @Throws(AnyException)
int parseIntStrict(String input) {
if (!RegExp(r'^\d+$').hasMatch(input)) {
throw FormatException("Invalid number format");
}
return int.parse(input);
}
|
@lrhn I think what most users want is not actually checked exceptions just a better way to signal to the tooling what exceptions are thrown, and for the tooling to actually recognize these signals. Take for example Note that the Additionally if you use the surround with try-catch it does not know what specific exceptions to catch. See that |
That would be the best approach IMO. Full fledged checked exceptions are hated by many (and loved by many for some reason), and would be a big change with many problems, as explained by a lot of people here and in the other issue about checked exceptions. However, having support through linters and tooling would be a seamless and more approachable (sic) approach. |
I agree that forcing exception checks is not ideal because as @mateusfccp stated, I would assume theres a significant enough number of users that do not always want that. I like thorough static checks but if I'm writing a quick short-lived script just to achieve a task then exceptional cases aren't normally a concern and having to constantly deal with them while just trying to quickly accomplish a task would be a frustrating experience. On the other hand if I'm developing a long-lived Flutter app then it is advisable to handle exceptional cases thoroughly and as close to their corresponding context as possible to ensure a greater user experience. For example, if I forgot to handle exceptions of an IO operation, I don't want that to crash the entire app. If an error occurred I would prefer to display a specific message to the user and perhaps perform some sensible default operation based on the specifics. In this case I want to be warned about places where I missed handling of exceptions. Being told by a static analyzer where exceptions can occur and are not being handled makes the job easier. I agree that a lint rule should be introduced that a project can enable to specifically detect a thrown In terms of technical feasibility, if automated control flow analysis would be too slow then we can go with the annotation approach of the defined function having to declare it throws. Although it would be convenient if the rule could just be opt-in and just have the code-base checked without requiring creators of API to broadcast whether or not their APIs throw or what their APIs throw. |
I guess add throws is no different than original error object in parameters. They are all contagious. |
I agree it's almost extremely contagious like The thing is the contagiousness is desirable for complex projects where exhaustive control over error response is wanted. But since it is not always desirable eg in a short tool script, it should be opt-in. |
I'd wager that the hate for checked exceptions is largely, if not mostly, a misdirection: that it's more about the lack of ergonomic error handling. When handling exceptions turns your code from this: final int example = int.parse("123"); into this: final int example;
try {
example = int.parse("123");
} on FormatException {
return;
} It's no wonder that many loathe the idea of checked exceptions, but it doesn't have to look like that. There's already an issue to introduce inline catching (#4205) hint hint, nudge nudge. The problem is that, as @lrhn mentioned, any function can throw, if only due to a stack overflow. But I do not necessarily think this matters: you'd be annotating a method with
And while I agree that |
With union types (#83) you can write something around the lines of: String | FormatException parseIntStrict(String input) {} |
Those kinds of union types would probably not be a good fit for Dart if only due to the arbitrary-object throwing that it inherited from Javascript. It's not encouraged and there's a linter for it, but it is nonetheless a supported language feature. How would one annotate that method to show that it returns a String or throws a String? Zig has a particular syntax for this where you could do (in Dart) However, it may be better to just use result types, which are already possible in Dart thanks to sealed classes. But since Dart provides no first-party result type, nor any language features to capture a return/throw into a result, what's left is something like |
why shouldnt it be breaking to add a new thrown type? its changing the behavior of the code isn't it? i hate the idea that a package might just decide a function throws all of a sudden in a patch version one its the one thing I actually hate about dart. everything is safe and sound... until it isn't. and I have no way of knowing. documentation is not the answer, because documentation does not affect code, (and no one documents to this level. we don't have the time for that.) but exceptions literally directly affect everything directly. this isn't a documentation problem, its a language problem. if we don't add a at least make
it could, nay should, be as convenient as we've had the argument about checked exceptions before, but we really need something and it has to be a language thing, or else we simply cant take advantage of it properly. no one is going to use an analyzer plugin unless it gets stupidly popular, which is unlikely. it neds to be quick-fixable too. any function with an explicit hell, for the lazy, if we wanted to go the extra mile, we could have an |
Dart currently lacks a way to explicitly annotate when a function or method is capable of throwing an error. This makes it difficult to reason about error handling in larger projects or when handling error on third-party packages.
I propose introducing a
throws
keyword (similar to Swift) that allows developers to declare when a function may throw, e.g.:This could help:
Brainstorm
As annotation
like async keyword
The text was updated successfully, but these errors were encountered: