-
Notifications
You must be signed in to change notification settings - Fork 630
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
Replace Extend
in Stream::collect
with custom FromStream
(a la FromIterator
)
#1833
Comments
Extend
in Stream::collect
with custom FromStream
(a la FromIterator
)
I think that this is a high priority issue, primarily because Ideally trait FromStream<A>: Sized {
async fn from_stream<S: Stream<Item = A>>(stream: S) -> Self;
} But of course, we don't have
trait FromStream<A>: Sized {
fn from_stream<'a, S: Stream<Item = A>>(stream: S) -> BoxFuture<'a, Self>;
} This is what
trait FromStream<A>: Sized {
type Buf;
fn initialize(size_hint: (usize, Option<usize>)) -> Self::Buf;
fn poll_from_stream<S: Stream<Output = A>>(
buf: &mut Self::Buf,
stream: Pin<&mut S>,
cx: &mut Context<'_>,
) -> Poll<Self>;
} The problem with this approach is that it is very limited. Control flow is hard because you can't store the stream anywhere. For example, when implementing it for
This is what tokio-stream does (as private API): pub trait FromStream<A>: Sized {
type Buf;
fn initialize(size_hint: (usize, Option<usize>)) -> Self::Buf;
fn extend(buf: &mut Self::Buf, val: A) -> ControlFlow;
fn finish(buf: Self::Buf) -> Self;
} This is very similar to the current All of these APIs feel subpar in comparison to
trait FromStream<A>: Sized {
type Output: Future<Output = Self>;
fn from_stream<S: Stream<Item = A>>(stream: S) -> Self::Output;
} The problem with this approach is that impl FromStream<Foo> for Bar {
type Output<A> = BarFromStreamFut<A>;
fn from_stream<S: Stream<Item = A>>(stream: S) -> Self::Output<S>;
} Instead, the future would have to box the stream, which suffers from the same issues as #1.
A solution to the above problem is to make trait FromStream<S: Stream> {
type Output: Future<Output = Self>;
fn from_stream(stream: S) -> Self::Output;
} However, generics with different associated types are still considered conflicting: // ERROR: conflicting implementations
impl<S: Stream<Item = char>> FromStream<S> for String { ... }
impl<S: Stream<Item = String>> FromStream<S> for String { ... } This means that
trait FromStream<A, S>
where
S: Stream<Output = A>,
{
type Output: Future<Output = Self>;
fn from_stream(stream: S) -> Self::Output;
} This solution works, and is forward compatible with
I still think that this approach has merit, and is least-breaking to change in the future when we get GATs or It's also worth noting that I'm working on a PR for the 6th method I mentioned, and it should be opened soon. Any thoughts? |
Hm.. so the problem with adding a second generic parameter means that you can't implement it for |
@taiki-e which API do you prefer? |
Hmm... My thought when I looked into this before was "there is no best way at this time". rust-lang/wg-async#15 (comment) |
Regarding option 4 outlined above, GATs have been stabilized as of Rust 1.65. Not sure if there is any appetite in moving forward with this approach in a next major/minor release of the
|
I'm puzzled on this one. I could understand Rust not being able to infer the type with how generic
Stream::collect
is but Rust can somehow infer it from the declaration if the type is notCopy
.Any idea what's going on here?
I should note that I've tried and reproduced this in 1.36.0 and nightly 2019-08-28.
Update: After talking this through with a colleague we figured out that the issue here is that
.collect()
inStream
depends onExtend
which has two possible implementations forCopy
types onVec
.Possible solution is to make a
FromStream
trait likeIterator
hasFromIterator
.The text was updated successfully, but these errors were encountered: