Description
The type void
in Dart works as a compile-time only marker indicating that the given value has no meaningful interpretation, and it should simply be discarded.
This works nicely for expressions of type void
, e.g., print('Hello!')
, which are accepted with no issues when used as an expression statement, but flagged as an error (not just a warning) if we try to use the value:
void main() {
print('Hello!'); // OK.
Object? o = print('Hello!'); // Compile-time error.
print('Hello!').toString(); // Compile-time error.
}
We allow an expression of type void
to occur in certain locations which are enumerated in an explicit allowlist.
However, several of the allowed positions are special in that they propagate the value which was obtained by evaluation of an expression of type void
, based on the rule, roughly, that the target has type void
as well.
void f(void _) => print('Hello!'); // Can return void to void.
void main() async {
void v = print('Hello!'); // Can initialize void variable.
v = print('Hello!'); // ... and assign to it.
f(print('Hello!')); // Can pass void to void parameter.
for (var x in <void>[]) { // Can iterate over void collection elements.
// However, `x` has type `void` so we (almost) can't use it.
}
return print('Hello!'); // Can return void to void in `{}` function, too.
// This one was turned into an error with null safety, so that problem is gone.
await print('Hello!'); // Compile-time error.
}
Some of these situations occur very rarely (for example, not many variables have declared type void
, and not many iterables have element type void
), but others come up repeatedly (=>
functions are widely used, and void
is a rather common return type).
This issue is intended to push us in the direction of having fewer situations where such values are propagated.
We could, for example, specify that an =>
function with return type void
returns null, no matter what, and we could eliminate the permission to use the value of a void
expression to initialize/assign a new value to a variable, or to return such a value.
We will still have loopholes, of course. For example, void
can be the value of a type parameter:
X f<X>(X x) {
X x2 = x; // Assigns void to void if the value of `X` is `void`.
return x2; // Returns void to void if the value of `X` is `void`
}
A proposal along these lines would be a breaking change (for instance, return e;
might need to be changed to e; return;
), but I still think that it will be useful to have an issue where this topic is discussed, and we might just do some of these things at some point. For example, I haven't heard any complaints about await print('Hello!')
being an error since Dart 2.12. ;-)