Custom Errors in Dart

It's possible to use custom types as errors by implementing the `toString()` method on them. Here's an example that uses a custom type to explicitly represent an argument error.

```dart
import 'dart:async';

// A custom error type usually has the suffix "Error".
class ArgError implements Exception {
  final int arg;
  final String message;

  ArgError(this.arg, this.message);

  // Adding this toString method makes ArgError implement
  // the Exception interface.
  @override
  String toString() => '$arg - $message';
}

Future<int> f(int arg) async {
  if (arg == 42) {
    // Return our custom error.
    throw ArgError(arg, "can't work with it");
  }
  return arg + 3;
}

void main() async {
  try {
    await f(42);
  } catch (e) {
    // In Dart, we use `is` to check the type of an exception
    if (e is ArgError) {
      print(e.arg);
      print(e.message);
    } else {
      print("err doesn't match ArgError");
    }
  }
}

To run the program:

$ dart run custom_errors.dart
42
can't work with it

In this Dart version:

  1. We define a custom ArgError class that implements the Exception interface.

  2. Instead of returning an error, we throw an exception in Dart.

  3. The f function is marked as async and returns a Future<int> to demonstrate error handling in asynchronous code.

  4. In the main function, we use a try-catch block to handle the exception.

  5. We use the is keyword to check if the caught exception is of type ArgError.

This example demonstrates how to create and use custom error types in Dart, which is conceptually similar to custom errors in other languages, but with syntax and conventions specific to Dart.