-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Stream<Y implements X> cannot be consumed by StreamConsumer<X> #37179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Consider the general case: import 'dart:async';
class A {}
class B implements A {}
class AConsumer implements StreamConsumer<A> {
@override
Future close() => null;
@override Future<void> addStream(Stream<A> stream) async => null;
}
main() async {
var stream = Stream.fromIterable([B()]);
await stream.pipe(AConsumer());
} The reason why this fails is that You can avoid this conflict if you can make sure that main() async {
var stream = Stream<A>.fromIterable([B()]);
await stream.pipe(AConsumer());
// or:
var stream2 = Stream.fromIterable([B()]);
await stream2.cast<A>().pipe(AConsumer());
} However, there is an underlying reason for this conflict which is a bit deeper: The class If it had been possible to declare the type argument of But given that we can't do that we can use the special case of invariance: Make sure that the same type argument is used everywhere (statically and dynamically). In general, it fits much better for Dart to use a function type (where parameter types are actually contravariant) than a class type, in this kind of situation where there is a type parameter that should have been contravariant, but that's of course not an option when using a class like |
Again, the situation doesn't allow for making changes to class A {}
class B implements A {}
class Stream<T> {
Stream.fromIterable(Iterable<T> elements);
Future pipe(StreamConsumer<Future Function(Stream<T>)> streamConsumer) =>
null;
}
typedef F<X> = Future Function(Stream<X>);
abstract class StreamConsumer<F0 extends F<Null>> {
F0 get addStream;
Future close();
}
class AConsumer implements StreamConsumer<F<A>> {
F<A> get addStream => null;
Future close() => null;
}
main() async {
var stream = Stream.fromIterable([B()]);
await stream.pipe(AConsumer());
} Now we have no problem passing an In any case, the tools are working as specified, and the fact that we cannot directly express contravariance for a type parameter is known. So I'll close this issue with 'working as intended'. |
Thanks for the response! Seems like I'll have some reading to do... Appreciate it. |
Thanks! |
2.3.2-dev.0.1 (Tue Jun 4 10:56:51 2019 +0200) on "macos_x64"
It seems that in newer versions of Dart, if I have a
Stream<Uint8List>
, I cannot use it with aStreamConsumer<List<int>>
. This worked in the past, and in my head makes sense - becauseUint8List
is a descendant ofList<int>
, anything that accepts aList<int>
should accept aUint8List
.I've included two examples here:
https://github.com/thosakwe/uint8_repro/tree/master/bin
Both cases result in an
argument_type_not_assignable
error.Uint8List
toList<int>
was already illegal in the analyzer before.2.2.0
.Stream.pipe
is not very useful without support for polymorphism. I can also imagine that this change could break code for a lot of Dart users. For example, I ran into this error while reading files frompackage:file
(usingFile.openRead
), and piping the resulting stream to aStreamConsumer<List<int>>
. In[email protected]
,Uint8List
is now returned instead ofList<int>
, and so anyone depending on that package (20 other packages on Pub) might already be running into this.Thanks in advance.
The text was updated successfully, but these errors were encountered: