Skip to content
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

Don't require Unpin when pin projecting. #89

Closed
wants to merge 1 commit into from

Conversation

najamelan
Copy link
Contributor

@najamelan najamelan commented Mar 4, 2020

There are two scenarios here. Either you want users to be able to pass in
streams that are !Unpin, in which case pin_project and the compiler
have your back and make sure this works, or...

You are going to require Unpin on it in which case you can remove the pin
projection. -> See #90

Both together don't make sense. Either one works here, but by removing the
Unpin the API is less restrictive, which I would favor, even if I don't
immediately see what concrete !Unpin type someone might want to call this with,
but sooner or later someone probably will, so why disable if it can work.

This leaves another problem though, there are many other places in the lib
where Unpin is required, notably on the wrappers around Stream, so it's
not said that with this change users can actually use any !Unpin streams.

There is one other use of pin-project on MidHandshake. As far as I can
tell the effect of this one is that Role::InternalStream can now be !Unpin
in this specific file, but is it even possible to use the lib with an !Unpin
type for this?

It looks like it might be good to rethink the choices about pin for the
entire lib and make something consistent. Maybe aim for the largest API
compatibility by trying to never require Unpin and seeing if pin-project
makes that possible.

In any case, the Unpin in Stream has no good reason for being there unless
you would like to get rid of pin-project.

As an added bonus, if you pin project, there is no need for re-pinning in the match.

There are two scenarios here. Either you want users to be able to pass in
streams that are `!Unpin`, in which case pin_project and the compiler
have your back and make sure this works, or...

You are going to require Unpin on it in which case you can remove the pin
projection.

Both together don't make sense. Either one works here, but by removing the
Unpin the API is less restrictive, which I would favor, even if I don't
immediately see what concrete `!Unpin` type someone might want to call this with,
but sooner or later someone probably will, so why disable if it can work.

This leaves another problem though, there are many other places in the lib
where `Unpin` is required, notably on the wrappers around `Stream`, so it's
not said that with this change users can actually use any `!Unpin` streams.

There is one other use of pin-project on `MidHandshake`. As far as I can
tell the effect of this one is that `Role::InternalStream` can now be `!Unpin`
in this specific file, but is it even possible to use the lib with an `!Unpin`
type for this?

It looks like it might be good to rethink the choices about pin for the
entire lib and make something consistent. Maybe aim for the largest API
compatibility by trying to never require `Unpin` and seeing if pin-project
makes that possible.

In any case, the Unpin in `Stream` has no good reason for being there unless
you would like to [get rid of pin-project](18cc347).

As an added bonus, if you pin project, there is no need for re-pinning in the match.
@najamelan
Copy link
Contributor Author

najamelan commented Mar 4, 2020

@sdroege if I'm not mistaken, you added pin projection here. I did spot the code first in async-tungstenite, so I think that has the same issues. If there is confusion about the pin API, don't hesitate to ask, but as long as mr unsafe is not involved, you can't kill puppies by accident.

@sdroege
Copy link
Contributor

sdroege commented Mar 4, 2020

@sdroege if I'm not mistaken, you added pin projection here.

Yeah, previously this was using unsafe code instead. I agree that getting rid of the Unpin bounds is a good goal, but I didn't have time to look into that myself yet.

@najamelan
Copy link
Contributor Author

Ok, I'll see if I can make time to look through the rest of the lib.

@najamelan
Copy link
Contributor Author

najamelan commented Mar 5, 2020

I started to look through both libs to see what it entails to work on underlying streams that are !Unpin. I sure think it's possible, but not straightforward. Take the close method on WebSocketStream for example. It calls SinkExt::send on self. This requires that Self is Unpin. So we can solve that with pin project, but then the method now needs to take self: Pin<&mut self>. That is quite un-ergonomic in an API, generally limited to low level API's like poll functions. Users will have to pin the WebSocketStream they want to use, and if it's !Unpin that requires unsafe. Maybe that's reasonable because they are the ones providing the !Unpin type to begin with, so supposedly they should know how to guarantee that they can pin it.

However, it's not ergonomic, it's a breaking change, and for what? Has anyone got a usecase for an !Unpin stream they might want to do websockets over? I must say I don't have the imagination right now to come up with a usecase. That doesn't mean there aren't any.

As a conclusion, I would lean towards keeping the Unpin requirement but removing pin-project from the equation as a consequence. I can file another PR for that. I would mention it in the docs, inviting people to file an issue if the bounds cause them trouble.

The choice is reversible with a breaking change, so rather than making a breaking change now and making the API less ergonomic, it might as well be done if and when a use case presents itself.

Stream::Plain(ref mut s) => Pin::new(s).poll_read(cx, buf),
Stream::Tls(ref mut s) => Pin::new(s).poll_read(cx, buf),
Stream::Plain(s) => s.poll_read(cx, buf),
Stream::Tls(s) => s.poll_read(cx, buf),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I do enjoy a bit simpler code here, I also find your thoughts on #90 reasonable, so I would probably prefer the approach from #90.

@daniel-abramov
Copy link
Member

Superseded by #194

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants