-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Add iterable variant of StreamMap
#8
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
Conversation
Refactor existing `StreamMap` into a `StreamMapInterable` that doesn't box the streams and allows iterating over the inner streams. Re-add type `StreamMap` as a wrapper around `StreamMapIterable` that does the old boxing and thus avoids breaking API.
StreamMapIterableStreamMap
StreamMapStreamMap
You can still downcast them back to what you had right? Perhaps we can offer an |
Sounds like either way you're restating the stream type in a type signature - either when you declare the type, or when you call the I think the But declaring a type usually only happens once per struct or function, and the compiler will warn you if they don't match. Another advantage of this PR is that the |
The difference is that in most cases - even in rust-libp2p for which I originally wrote this code - you don't pass a name-able If we add an
I'd expect that you only use the |
|
Thank you for explaining! I'm not sure if we're talking about the same thing here?
I think we agree about that. But in this PR, Only Let's try some examples from the libp2p PR: With this PR: let iterable_streams: StreamMapIterable<UniqueConnecId, InboundSubstreamState> = ...;
let found_stream = iterable_stream.iter().find(|(id, state)| ...);
...
let another_found_stream = iterable_stream.iter().find(|(id, state)| ...);
let boxed_streams: StreamMap<UniqueConnecId, ConnectionHandlerEvent<...>> = ...;What I think you're suggesting: let iterable_streams: StreamMap<UniqueConnecId, ConnectionHandlerEvent<...>> = ...;
let found_stream = iterable_streams.iter::<InboundSubstreamState>().find(|(id, maybe_downcast_state)| if let Some(state) = maybe_downcast_state { ... });
...
let another_found_stream = iterable_streams.iter::<InboundSubstreamState>().find(|(id, maybe_downcast_state)| if let Some(state) = maybe_downcast_state { ... });
let boxed_streams: StreamMap<UniqueConnecId, ConnectionHandlerEvent<...>> = ...;The second example looks slightly more complex than the first to me: it has more types, and more checks. Which is why I'm not sure if we're talking about the same thing.
Your explanation makes sense, but I don't see an ergonomic hit with the code in this PR. |
But it duplicates the code! If we just add the
Not quite. I was thinking we do the downcasting for the user so if you call |
Yep, code duplication is a downside, and so is having to choose between types with similar APIs but different type parameters. Even if
Sorry, still not quite sure what you mean here. Maybe if I don't get it this time, you could provide an example? Do you mean users having to choose between this PR's Or something that's not in this PR, like
Ah, so downcast and flatten. That makes sense, and would be more ergonomic in the happy case where the types were all correct. |
|
I should have probably spelled this out more clearly. I don't want to provide two versions of So if we take that as a precondition and want to enable Given that most users won't use This can also be framed as an additional advantage if you want to store multiple different stream types in the same |
Makes sense! Edit: thanks for your patience here. @elenaf9 just confirming that Currently in libp2p/rust-libp2p#6009 it would be easiest to use But I can understand wanting to change to |
I am happy for this change to land on both, or even all types of this crate given that they all follow the same pattern. |
|
TLDR: I would prefer having a type-safe interface rather than downcasting, but I am also fine with the latter. But I am not able to make that work, so some pointers would be great @thomaseizinger. I initially tried to solve it with downcasting, but wasn't able to make it work.
I understand that perspective, but I am not sure I agree. Also,
I think for streams specifically (not really for futures) it is actually quite common to implement
|
|
|
It took me a bit of playing around but I managed to make it work: #9 Feel free to build on top of that! |
For libp2p/rust-libp2p#6009 it would be great if we could iterate through the streams in a
futures_bounded::StreamMap.Just implementing an iterator for that type unfortunately doesn't work because
StreamMapboxes its inner streams and thus obscures the stream type.This PR adds
StreamMapIterableas a variant ofStreamMapthat doesn't do any boxing and implementsiteranditer_mut.Implementation wise,
StreamMapIterableis just the oldStreamMapbut without boxing of streams, andStreamMapa wrapper around it.Notes / Open Questions
If we want be consistent with the rest of rust-libp2p, we should technically call the
StreamMapIterablejustStreamMap, and the oldStreamMapwith the boxingStreamMapBoxed. But that would a) break the API and b) also require to change all other types for consistency. I'd be fine with that as well, but went with the minimal change for now. Wdyt @thomaseizinger @jxs?