Description
I may be confused, but my understanding and intuition is that void
means nothing
, and something of type void
cannot take on any values. This understanding is supported by the way that Dart doesn't let you return anything from void
-returning methods:
void foo1() {
return 0; // "The return type 'int' isn't a 'void', as defined by the method 'foo1'"
}
It also doesn't let you read anything from arguments that are explicitly typed void
:
import 'dart:async';
void main() {
final Completer<void> completer = new Completer<void>();
completer.complete(0);
completer.future.then((void value) {
print(value); // "The expression here has a type of 'void', and therefore cannot be used"
});
}
(In both these cases, these are only analyzer restructions; the compiler allows both. But that's fine, so long as one of them complains, I consider it "invalid Dart".)
Somehow, though, this doesn't always hold. For example, you can return a value from an async
method that returns Future<void>
:
Future<void> foo2() async {
return 0; // no error (!!)
}
You can also pass values to methods expecting void
with no ill effects:
void main() {
final List<void> a = <void>[];
a.add(0); // no error at analysis time or runtime (!!)
print(a); // prints [0] (!!)
}
According to the analyzer, you can pass "void"-expecting objects into APIs that expect other types, though that is a runtime error in Dart2 (not Dart1):
void addNumber(List<int> list) {
list.add(0);
}
void main() {
final List<void> list = <void>[];
addNumber(list); // no analyzer error, though the Dart2 runtime does throw here
print(list);
}
You can pass non-void expecting objects into APIs that expect void objects, with unexpected results:
void printVoidList(List<void> list) {
print(list); // prints [0] (!!)
}
void main() {
printVoidList(const <int>[0]); // no error at analysis time or runtime (!!)
}
This leads to situations where you can be completely messing up the types, but neither the analyzer nor the compiler will complain. This code is based on actual code in the Flutter framework that tries to use generics to catch programmer errors, but currently fails to catch any errors because of this:
import 'dart:async';
List<Completer<dynamic>> pendingWork = <Completer<dynamic>>[];
int addWork<T>(Completer<T> work) {
pendingWork.add(work);
return pendingWork.length - 1;
}
void completeWork<T>(int index, T value) {
pendingWork[index].complete(value);
}
void main() {
Completer<int> completer = new Completer<int>();
int index = addWork<void>(completer); // no error (!!)
completeWork<void>(index, 0); // no error (!!)
completer.future.then(print); // prints "0"
}