0

Once in a while we all need to quickly return multiple values from a function, and look for a way to create a new type on the fly.

In Python I can return a tuple

def get_trio1():
    return (True, 23, "no")


(_, first1, second1) = get_trio1()
print(first1)
print(second1)

ignore one of the values, and retrieve both of the other two on the fly in one assignment.

I can likewise return an array.

def get_trio2():
    return [True, 23, "no"]


[_, first2, second2] = get_trio2()
print(first2)
print(second2)

But both of these are brittle. If I edit the code to add a value, particularly if it's within the three already defined, the revised code could fail silently.

Which is why the nicest solution is to create a dict on the fly.

def get_trio3():
    return {"b": True, "i": 23, "s": "no"}


r = get_trio3()
print(r["i"])
print(r["s"])

The use of named members means that maintaining the code is considerably safer.

What is the closest I can do to get the same safety in Dart? Is defining a class for the return type necessary?

In case it matters, the context is avoiding List<List<dynamic>> when returning a future.

Future<List<dynamic>> loadAsset() async =>
  return await Future.wait([
    rootBundle.loadString('assets/file1.txt'),
    rootBundle.loadString('assets/file2.txt'),
  ]);

Update

Using Stephen's answer for a future introduces a problem. Future.wait is hardwired to use an array Iterable.

Future<Map<String, dynamic>> loadAsset() async =>
  return await Future.wait({
      "first": rootBundle.loadString('assets/file1.txt'),
      "second": rootBundle.loadString('assets/file2.txt'),
});
Sam
  • 563
  • 5
  • 15

2 Answers2

1

Use a map.

Map<String, dynamic> foo() => {'A': 'a', 'B': 'b', 'C': false }

var result = foo();
print result['B']; // prints b;
Stephen
  • 4,041
  • 22
  • 39
  • Cool.. that does it. Can you just confirm that this is not actually usable with `Future.wait`? I updated the question with your answer. – Sam Apr 15 '21 at 17:58
1

Your loadAsset function returns a Future<List<dynamic>> because that's how you declared it. You could have declared it to return a Future<List<String>> instead.

Future.wait is hardwired to use an array.

Especially since Dart is a statically-typed language, you can't really expect it to take both a List and some Map with your custom semantics. You could write your own version:

Future<Map<String, T>> myFutureWait<T>(Map<String, Future<T>> futuresMap) async {
  var keys = futuresMap.keys.toList();
  var values = futuresMap.values.toList();
  var results = await Future.wait<T>(values);
  return Map.fromIterables(keys, results);
}
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • Super. I have two quick sequels. 1- This works, but it's too much like magic. When I call `myFutureWait` with a `Map` with each value in the `Map` of the form `rootBundle.loadString('assets/file_n.txt')`, what stops the two values from executing right there at the point of defining the map? And if they do execute at the point of call, how can it work when they're not yet wrapped in a `Future.wait`? 2- The real reason why I can't use a Map is that a Map in Dart (surely for some reasons) is not an Iterable, whereas a List is; no? – Sam Apr 16 '21 at 01:38
  • 1
    1. `AssetBundle.loadString()` returns a `Future`. Nothing technically prevents `loadString()` from doing its worth synchronously and immediately returning an already-completed `Future` with the result, but that would be silly and would defeat the point of returning a `Future` in the first place. 2. If you want to iterate over a `Map`, you can easily iterate over [`Map.entries`](https://api.dart.dev/stable/dart-core/Map/entries.html). – jamesdlin Apr 16 '21 at 01:58
  • 1
    And the reason you can't pass a `Map` to `Future.wait` is because `Future.wait` expects each element of the `Iterable` to be a `Future`. Each "element" of a `Map` consists of two objects: a key and a value. Now, each of those values might be a `Future`s (in which case you can do `Future.wait(map.values)`, which is basically what I did in the example code above), but that's a specialized case. – jamesdlin Apr 16 '21 at 02:11