Dart VM version: 2.0.0-dev.67.0 (Tue Jul 3 18:17:07 2018 +0200) on “macos_x64”
Let’s assume following code.
import 'dart:async';
class Zoo {
Map<String, Animal> animals = <String, Animal>{};
void add(Animal animal) => animals[animal.name] = animal;
Animal get(String name) => animals[name];
Cat getCat(String name) => get(name);
Dog getDog(String name) => get(name);
Future<Animal> asyncGet(String name) async => await new Future<Animal>.value(animals[name]);
Future<Cat> asyncGetCat(String name) async => await asyncGet(name);
Future<Dog> asyncGetDog(String name) async => await asyncGet(name);
Future<Cat> badAsyncGetCat(String name) => asyncGet(name);
Future<Dog> badAsyncGetDog(String name) => asyncGet(name);
String toString() => "Zoo: $animals";
}
class Animal {
String name;
String toString() => "$runtimeType: $name";
}
class Cat extends Animal {
void meow() => print("$name: Meow !");
}
class Dog extends Animal {
void waouf() => print("$name: Waouf !");
}
Future<Null> main() async {
var zoo = new Zoo()..add(new Animal()..name = 'Foo')..add(new Cat()..name = 'Felix')..add(new Dog()..name = 'Brutus');
print(zoo);
// Sync zoo
{
var animal = zoo.get('Felix');
print(animal.runtimeType);
var cat = zoo.getCat('Felix');
print(cat.runtimeType);
var dog = zoo.getDog('Brutus');
print(dog.runtimeType);
var nobody = zoo.getCat('Nobody');
print(nobody.runtimeType);
}
// Async zoo
{
var animal = await zoo.asyncGet('Felix');
print(animal.runtimeType);
var cat = await zoo.asyncGetCat('Felix');
print(cat.runtimeType);
var dog = await zoo.asyncGetDog('Brutus');
print(dog.runtimeType);
var nobody = await zoo.asyncGetCat('Nobody');
print(nobody.runtimeType);
}
// Bad async zoo
{
var cat = await zoo.badAsyncGetCat('Felix');
print(cat.runtimeType);
var dog = await zoo.badAsyncGetDog('Brutus');
print(dog.runtimeType);
var nobody = await zoo.badAsyncGetCat('Nobody');
print(nobody.runtimeType);
}
}
The output result is following.
$ dart animal.dart
Zoo: {Foo: Animal: Foo, Felix: Cat: Felix, Brutus: Dog: Brutus}
Cat
Cat
Dog
Null
Cat
Cat
Dog
Null
Cat
Unhandled exception:
type 'Future<Animal>' is not a subtype of type 'Future<Cat>'
#0 Zoo.badAsyncGetCat (async.dart:17:46)
#1 main (file:///async.dart:75:25)
#2 _RootZone.runUnary (dart:async/zone.dart:1381:54)
#3 _FutureListener.handleValue (dart:async/future_impl.dart:129:18)
#4 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:638:45)
#5 Future._propagateToListeners (dart:async/future_impl.dart:667:32)
#6 Future._completeWithValue (dart:async/future_impl.dart:482:5)
#7 Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:512:7)
#8 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#9 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#10 _runPendingImmediateCallback (dart:isolate/runtime/libisolate_patch.dart:113:13)
#11 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:166:5)
I understand well the thrown exception.
I would like to raise the point, because I found the “workaround” with async/await that sounds weird.
So, as Future is a kind of special type, I suggest that the inner type propagation should be allowed for Future.
What do you think about it ?
It seems silly to specialize this case. I wouldn’t expect, for example:
… to succeed, so specializing it for
async
functions is just asking for confused users to try insertingasync
everywhere` until it does.