Closed dylanede closed 3 years ago
@dylanede does this looks like sufficient documentation? https://github.com/remexre/warp/commit/c65678b0125247c46cb0a3fac251f1f8b5756a2d (will PR if so)
@remexre reading that PR - I still don't understand the situation into which to use into_a
and into_b
, and the docs here:
https://docs.rs/warp/0.1.20/warp/filters/sse/index.html#example confuse me even more D: Why is into_a().into_b()
and into_b().into_b()
a thing?!
This needs rewording to sound more documentationy, but:
into_a
and into_b
serve equivalent purposes: to allow returning multiple ServerSentEvent
types without needing a trait object. One could write either:
fn foo() -> Box<dyn ServerSentEvent> {
if something() {
data("foo").boxed()
} else {
comment("foo").boxed()
}
}
or
fn bar() -> EitherServerSentEvent<impl ServerSentEvent /* from data */, impl ServerSentEvent /* from comment */> {
if something() {
data("bar").into_a()
} else {
comment("bar").into_b()
}
}
// or, since EitherServerSentEvent: ServerSentEvent
fn bar2() -> impl ServerSentEvent {
if something() {
data("bar2").into_a()
} else {
comment("bar2").into_b()
}
}
Depending on the types involved, the latter can be more efficient. (Whether it's efficient enough to be measurable is a different question...)
One might chain .into_a().into_b()
to be able to return more than two different types. For example,
fn baz() -> EitherServerSentEvent<EitherServerSentEvent<impl ServerSentEvent /* from data */, impl ServerSentEvent /* from comment */>, impl ServerSentEvent /* from event */> {
if something() {
data("baz").into_a().into_a() // A(A(data("baz")))
} else if something_else() {
comment("baz").into_b().into_a() // A(B(comment("baz")))
} else {
event("baz").into_b() // B(event("baz"))
}
Sorry for dropping in, I am a Rust novice, playing around with Warp. I got stuck on this when running the Sse. examples.
Please correct if any of the below is wrong.
Thanks to the above answers I figured out the intent (avoid boxing by building a fixed size type, I kind of guessed that was the purpose since the boxed function is in the same trait, but couldn't really move forward ) but not how/why it works language wise. I assume that Rust compiler somehow calculate an EitherServerSentEvent<A, B> that is correct for all possible return types?
Is there some compiler documentation that tells how this works? Is there some academic term for this that one can search for?
This is what is confusing to me:
// From src/filters/sse.rs
/// Convert to either A
fn into_a<B>(self) -> EitherServerSentEvent<Self, B> {
EitherServerSentEvent::A(self)
}
/// Convert to either B
fn into_b<A>(self) -> EitherServerSentEvent<A, Self> {
EitherServerSentEvent::B(self)
}
when calling data("baz").into_a().into_a(), how is the B type resolved? What does it mean? I realise this is not a warp specific question.
The A(A(*)) explanation only confused me more (sorry, I am sure it is helpful to others), but triggered me to rewrite it using types instead, which made me I realise how things could be solved, to figure out one type that satisfies all cases.
// shortening EitherServerSentEvent -> ESSE
fn baz() -> EitherServerSentEvent<EitherServerSentEvent<impl ServerSentEvent /* from data */, impl ServerSentEvent /* from comment */>, impl ServerSentEvent /* from event */> {
if something() {
data("baz").into_a().into_a() // ESSE<ESSE<SseData, _> _>
} else if something_else() {
comment("baz").into_b().into_a() // ESSE<ESSE<_,SseComment,> _>
} else {
event("baz").into_b() // ESSE<_,SseEvent>
}
// using _ to mean a wildcard type, the (only?) possible solution is ESSE<ESSE<SseData, SseComment>, SseEvent>
Is there some compiler documentation that tells how this works? Is there some academic term for this that one can search for?
It looks like you're using it right below as far as I can tell. This is a consequence of unification in Hindley-Milner type inference.
EDIT: The Wikipedia article doesn't do a great job of explaining Hindley-Milner, now that I skim it; I'd recommend googling around elsewhere. As a self-plug for a resource, I have an implementation of Hindley-Milner in Rust as part of another project; it's Rust 2015 code, but as far as I know, it doesn't use much that's changed since.
I presume benchmarks have been run at some point demonstrating that dyn
is a net negative? If not, it should be just to confirm a suspicion.
I believe we should probably redesign the sse
module to work more like ws
does.
How difficult do you think a redesign would be for someone who hasn't touched warp's code before? I've worked on Rocket a bit in the past.
I don't think either are that complicated. Probably the sse
is too clever, in hindsight. For a newcomer to rust, I'd say skip. It's probably medium difficulty.
it seems this can now be closed as https://github.com/seanmonstar/warp/pull/663 has been merged and ServerSentEvent::into_a
and ServerSentEvent::into_b
have been deprecated
These functions (
ServerSentEvent::into_a
andServerSentEvent::into_b
) seem very opaque and their documentation is not very helpful as to what they are used for. The example code for thesse
module uses them but does not explain what they are doing. It would be helpful if the documentation was improved.