Generics in Dart

Our example demonstrates the use of generics in Dart. Dart has supported generics since its early versions, allowing for type-safe generic programming.

import 'dart:collection';

// As an example of a generic function, `slicesIndex` takes
// a list of any type T and an element of that type and returns
// the index of the first occurrence of v in s, or -1 if not present.
int slicesIndex<T>(List<T> s, T v) {
  return s.indexOf(v);
}

// As an example of a generic type, `LinkedList` is a
// singly-linked list with values of any type.
class LinkedList<T> {
  _Node<T>? _head;
  _Node<T>? _tail;

  // We can define methods on generic types just like we
  // do on regular types.
  void push(T v) {
    final newNode = _Node(v);
    if (_tail == null) {
      _head = _tail = newNode;
    } else {
      _tail!.next = newNode;
      _tail = newNode;
    }
  }

  // AllElements returns all the List elements as a list.
  List<T> allElements() {
    final elements = <T>[];
    var current = _head;
    while (current != null) {
      elements.add(current.value);
      current = current.next;
    }
    return elements;
  }
}

class _Node<T> {
  T value;
  _Node<T>? next;
  _Node(this.value);
}

void main() {
  var s = ['foo', 'bar', 'zoo'];

  // When invoking generic functions, we can often rely
  // on type inference. Note that we don't have to
  // specify the type for T when calling `slicesIndex` -
  // the compiler infers it automatically.
  print('index of zoo: ${slicesIndex(s, 'zoo')}');

  // ... though we could also specify it explicitly.
  slicesIndex<String>(s, 'zoo');

  final lst = LinkedList<int>();
  lst.push(10);
  lst.push(13);
  lst.push(23);
  print('list: ${lst.allElements()}');
}

To run the program, save it as generics.dart and use the dart command:

$ dart run generics.dart
index of zoo: 2
list: [10, 13, 23]

In this Dart example, we’ve implemented similar functionality to the Go code:

  1. The slicesIndex function is a generic function that finds the index of an element in a list. Dart’s standard library already provides this functionality with the indexOf method, so we’ve used that in our implementation.

  2. We’ve created a generic LinkedList class to demonstrate generic types. This is similar to the List type in the Go example.

  3. The push and allElements methods on LinkedList demonstrate how to work with generic types in method implementations.

  4. In the main function, we show how to use these generic constructs, including type inference when calling generic functions.

Dart’s generics are similar to Go’s in many ways, but there are some differences:

  • Dart has had generics for a longer time, so they’re more integrated into the language and standard library.
  • Dart uses angle brackets <> for generic type parameters, while Go uses square brackets [].
  • Dart doesn’t have an equivalent to Go’s ~ operator for type constraints. Instead, Dart uses extends keyword for upper bounds.

This example showcases how Dart’s generics allow for flexible, type-safe code that can work with multiple types while maintaining compile-time type checking.