deepCopy method

Iterable<T> deepCopy({
  1. bool growable = true,
})

Returns a new List or Set that contains a deep copy of every element from this.

IMPORTANT: Creating a deep copy of List<Map<String, int>>, Set<Map<String, int>> or any other Iterable<Map<String, int>> only works with MyUtilityExtensionDeepCopyIterable.deepCopyDynamic.

This is because of a restriction in how generic type information can be preserved when copying a Map.

Implementation

Iterable<T> deepCopy({bool growable = true}) {
  final copy = _SetOrList<T>.copy(this);

  // Clear the copied list.
  copy.clear();

  // Deep copy every item and add it to [copy].
  for (final item in this) {
    final copiedItem = _deepCopy<T>(item);

    try {
      copy.add(copiedItem);
    } catch (e) {
      if (e is! TypeError) rethrow;

      final msg = e.toString();
      final prefix =
          "type '${copiedItem.runtimeType}' is not a subtype of type 'Map";
      if (!msg.startsWith(prefix)) rethrow;

      // Extract expected type information from error message.
      final mapTypesMatch = RegExp(r"'Map<(.+),\s*(.+)>'").firstMatch(msg)!;
      final mapTypeKey = mapTypesMatch[1]!;
      final mapTypeValue = mapTypesMatch[2]!;

      // TODO(obemu): Somehow preserve generic types when copying maps.
      //
      // Preserving generic type information with [Map.of] or any other [Map]
      // constructor is currently **only possible** if the constructor is used
      // in the **same scope where the source object** has been declared,
      // which means we loose generic type information in nested calls to
      // deepCopy.
      //
      // Sadly there is no native `Map.toMap()` method, like the
      // `Iterable.toList()` method that preserves generic type information.
      // There also is no native `Map.clone` method. There is a hack to
      // simulate `Map.toMap()`, but it depends on 'dart:isolate', is async
      // and quite smelly:
      //
      // ```dart
      // import 'dart:isolate';
      //
      // extension MapCloning<K,V> on Map<K,V> {
      //   Future<Map<K,V>> toMap()() {
      //     final port = ReceivePort();
      //     try {
      //       port.sendPort.send(this);
      //       return port.first as T;
      //     } finally {
      //       port.close();
      //     }
      //   }
      // }
      // ```
      //
      // There also seems to be no incentive on the side of the Dart team to
      // introduce a proper way for this, see:
      //
      // * https://github.com/dart-lang/sdk/issues/50634
      // * https://github.com/dart-lang/sdk/issues/45033
      // * https://github.com/dart-lang/sdk/issues/50634

      throw UnsupportedError(
        "Cannot create a deep copy of a nested "
        "Map<$mapTypeKey, $mapTypeValue>, without a user provided "
        "DeepCopyFactory<Map<$mapTypeKey, $mapTypeValue>>.\n"
        "\n"
        "Copying nested maps without a user provided [DeepCopyFactory] is "
        "currently not supported,\n"
        "because of how the Dart SDK implements the [Map.of] and [Map.from] "
        "constructors. The\n"
        "current implementation makes it impossible to preserve "
        "generic type information when\n"
        "copying a Map. This causes issues when trying to add the "
        "copied Map to an Iterable\n"
        "that has preserved its generic type information.\n"
        "\n"
        "Error from dart:core lib:\n$msg",
      );
    }
  }

  return growable ? copy.inner : copy.toUnmodifiable();
}