Dart

Dart Patterns and Exceptions

Module Still Under Development

# Patterns and Exceptions

Patterns are a new Dart feature that includes the ability of destructuring. Patterns were added in version 3.0 of Dart. Patterns are a syntactic category in the Dart language, like statements and expressions. A pattern represents the shape of a set of values that it may match against actual values.

In general, a pattern may match a value, destructure a value, or both, depending on the context and shape of the pattern.

First, pattern matching allows you to check whether a given value:

  • Has a certain shape.
  • Is a certain constant.
  • Is equal to something else.
  • Has a certain type.

Then, pattern destructuring provides you with a convenient declarative syntax to break that value into its constituent parts. The same pattern can also let you bind variables to some or all of those parts in the process.

# Pattern Matching and Destructuring

Patterns can be used for matching. A common example would be with a switch-case statement. You can match against a constant pattern, which is what you are used to doing by providing a single value.

switch(number) {
  case 1:
    print('matches the integer 1');
}
1
2
3
4

There are also logical patterns with && or ||.

switch (color) {
  Color.red || Color.blue || Color.yellow => true,
  _ => false
};
1
2
3
4

You can match a List too:

List<String?> row = ['user', null];
switch (row) {
  case ['user', var name!]: // ...
  // 'name' is a non-nullable string here.
}
1
2
3
4
5

For a full list of Pattern types see this reference

# Destructuring

Destructuring works in a similar way to how it works in JavaScript. Part of the difference is how datatypes are usually added.

Here is an example with a variable, of type record, where its contents are typed as num and Object. Then, with destructuring, a pattern is matched along with the ability to cast the variables to int and String. The num becomes an int and the Object becomes a String. The types have to make sense, and become more specific.

(num, Object) record = (1, 's');
var (i as int, s as String) = record;
1
2

You can destructure a List just like you would destructure an Array in JavaScript.

var numList = [1, 2, 3, 4, 5];
// List pattern [a, b, c] destructures the elements from numList...
// and the underscores each mean to skip and not accept a value
var [a, _, _, d, e] = numList;
1
2
3
4

You can destructure in a for..in loop.

Map<String, int> hist = {
  'a': 23,
  'b': 100,
};
//destructure a Map
for (var MapEntry(key: key, value: count) in hist.entries) {
  print('$key occurred $count times');
}
1
2
3
4
5
6
7
8

Object patterns match against named object types, allowing you to destructure their data using the getters the object’s class already exposes.

To destructure an instance of a class, use the named type, followed by the properties to destructure enclosed in parentheses:

final Foo myFoo = Foo(one: 'one', two: 2);
var Foo(:one, :two) = myFoo;
print('one $one, two $two');
1
2
3

Map and list patterns work well for destructuring key-value pairs in JSON data:

var json = {
  'user': ['Lily', 13]
};
var {'user': [name, age]} = json;
1
2
3
4

# Exceptions and Errors

Dart provides Exception and Error types, as well as numerous predefined subtypes. You can, of course, define your own exceptions. However, Dart programs can throw any non-null object—not just Exception and Error objects—as an exception. Exceptions are errors indicating that something unexpected happened.

The Error class creates Error objects thrown in the case of a program failure. An Error object represents a program failure that the programmer should have avoided. Examples include calling a function with invalid arguments, or even with the wrong number of arguments, or calling it at a time when it is not allowed. These are not errors that a caller should expect or catch — if they occur, the program is erroneous, and terminating the program may be the safest response.

The Exception class creates objects that are intended to convey information to the user about a failure, so that the error can be addressed programmatically. It is intended to be caught, and it should contain useful data fields. There is the base Exception class, but additionally there are more specific types including FormatException, IOException, TimeoutException, and NullRejectionException.

You can create your own classes that implements Exception so you have custom exceptions that pertain to your app.

class FellDownException implements Exception {
  FellDownException(String message);
}
1
2
3

# Try Catch Throw

Just like in JavaScript there is a throw keyword that you can use to trigger an error state which will pause the current thread (or isolate) and, if you have a try catch be handled by it.

try {
  //run some code attempt here
  throw 'A basic error message';
}on FormatException {
  //handle any FormatException
}on Exception catch(ex){
  //handle a general Exception
}catch(ex, st){
  //handle anything else that was not handled yet
  print('Exception details: \n$ex');
  print('Stack trace: \n$st')
}finally{
  //run this code after the try plus any triggered `on` or `catch`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Unlike Javascript, Dart can create a chain of Exception handling statements. Each specific Exception will use the on keyword.

You can also use on Exception catch() to handle any general exception that was not specifically mentioned in the preceding on statements.

The simple catch() statement after on Exception catch() handles any exception not previously caught, as well as, anything else that was thrown (String, Object, etc).

At the very end you can add a finally statement. This is a grouping of code that runs whether the code in try worked or failed.

# Hybrid Modules

Last Updated: 8/30/2023, 12:50:03 PM