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

Why is the Connection boxed by default? #45

Closed
NeoLegends opened this issue May 5, 2019 · 13 comments · Fixed by #249
Closed

Why is the Connection boxed by default? #45

NeoLegends opened this issue May 5, 2019 · 13 comments · Fixed by #249

Comments

@NeoLegends
Copy link

Hey,

I was just skimming over the API and I was wondering why connect and accept return a boxed Connection? Connection is neither a trait object nor dynamically sized, so why the need for the box?

@NeoLegends NeoLegends changed the title Why Result<Box<Connection>>? Why is the Connection boxed by default? May 5, 2019
@ghedo
Copy link
Member

ghedo commented May 7, 2019

Hello,

Connection is self-referencing, so it can't be moved or the internal reference (which is actually a raw pointer) would become invalid. So we use Box to get a more stable address on the heap. In theory we should use Pin<Box<...>> but Pin doesn't expose the APIs we need.

@ghedo ghedo closed this as completed May 7, 2019
@NeoLegends
Copy link
Author

NeoLegends commented May 7, 2019

Ahh, thanks for the clarification. One more question: could this be hidden inside Connection‘s API?

This feels like a nitty gritty API internal to me that shouldn‘t be exposed to the user (at least from my limited understanding). Especially because the user might be tempted to move the connection out of the box, effectively breaking it. For example, Connection could be made into a newtype around the current Connection.

And if that‘s not possible, I think it should at least be documented that the boxing is intentional. :)

@ghedo
Copy link
Member

ghedo commented May 9, 2019

I'm not sure the indirection of wrapping Connection would be worth the effort, though I do agree that this should be documented. I'll reopen the issue to track that part.

@ghedo ghedo reopened this May 9, 2019
@NeoLegends
Copy link
Author

I could file a PR to do this. I'd just need some guidance as to what to do with the documentation on Connections methods. Should they move to the newtype or be copied (so that they'd exist twice)? And also, would you be fine with moving the connection implementation from lib.rs into a separate module, so that lib.rs solely wraps Connection into a newtype and exports the public API surface?

@ghedo
Copy link
Member

ghedo commented May 14, 2019

@NeoLegends it's not only about actually having to do the initial work to implement that, but the maintainance burden the change adds in the future (e.g. the public Connection APIs would need to be duplicated to handle the wrapped, which makes it somewhat harder to understand and add new APIs).

So for the moment I'd rather not do anything more than documenting the current situation. We can always reconsider in the future.

@RReverser
Copy link
Contributor

e.g. the public Connection APIs would need to be duplicated to handle the wrapped

I don't think this is true - you already accept it by reference, and it will be perfectly fine to pass pointer from Box as if it was a pointer to unwrapped structure.

@ghedo
Copy link
Member

ghedo commented May 14, 2019

@RReverser if I understand correctly, the proposal here is to have something like pub struct ConnectionWrapper(Box<Connection>) and make Connection itself private, so you'd have to mirror the private Connection API in ConnectionWrapper.

@NeoLegends
Copy link
Author

NeoLegends commented May 14, 2019

@ghedo Correct. I think though, that the additional maintenance overhead will be minimal and that the safety gains outweigh this. When I made my first tests (and bumped into the questions I asked), the newtype added a mere 100 lines or so to lib.rs, not including some one-time churn like converting connect / accept to return the newtype and so on.

@cramertj
Copy link

Connection is self-referencing, so it can't be moved or the internal reference (which is actually a raw pointer) would become invalid. So we use Box to get a more stable address on the heap. In theory we should use Pin<Box<...>> but Pin doesn't expose the APIs we need.

FWIW there's nothing stopping users from pulling the connection out of the box and putting it back in another one, triggering UB. What specific APIs do y'all need that Pin<Box<Connection>> wouldn't allow?

@ghedo
Copy link
Member

ghedo commented Jul 2, 2019

@cramertj basically this rust-lang/rust#60245, though it could potentially be implemented in some other uglier way.

@NeoLegends
Copy link
Author

@ghedo i believe the required features will be stabilized as part of 1.39. rust-lang/rust#63985

@ghedo
Copy link
Member

ghedo commented Sep 28, 2019

Yep, I made that PR :)

ghedo added a commit that referenced this issue Nov 7, 2019
@ghedo ghedo mentioned this issue Nov 7, 2019
@ghedo
Copy link
Member

ghedo commented Nov 7, 2019

Made #249 based on Rust 1.39.

ghedo added a commit that referenced this issue Nov 7, 2019
ghedo added a commit that referenced this issue Nov 12, 2019
Due to the fact that we store a pointer to the Connection object inside
its own Handshake context, Connection objects cannot be moved (which is
why we return a boxed object from its constructor). Using Pin on it
makes the compiler enforce this.

Fixes #45.
ghedo added a commit that referenced this issue Nov 12, 2019
Due to the fact that we store a pointer to the Connection object inside
its own Handshake context, Connection objects cannot be moved (which is
why we return a boxed object from its constructor). Using Pin on it
makes the compiler enforce this.

Fixes #45.
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 a pull request may close this issue.

4 participants