-
Notifications
You must be signed in to change notification settings - Fork 182
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
Avoid boxing in Connector chains #919
Conversation
f977ff5
to
3b63fef
Compare
7fafe3a
to
2c2af87
Compare
pub struct ChainedConnector<In, First, Second>(First, Second, PhantomData<In>); | ||
|
||
impl<In, First, Second> Connector<In> for ChainedConnector<In, First, Second> | ||
where | ||
In: Transport, | ||
First: Connector<In>, | ||
Second: Connector<First::Out>, | ||
{ | ||
type Out = Second::Out; | ||
|
||
fn connect( | ||
&self, | ||
details: &super::ConnectionDetails, | ||
chained: Option<In>, | ||
) -> Result<Option<Self::Out>, crate::Error> { | ||
let f_out = self.0.connect(details, chained)?; | ||
self.1.connect(details, f_out) | ||
} | ||
} | ||
|
||
impl<In, First, Second> ChainedConnector<In, First, Second> { | ||
pub(crate) fn new(first: First, second: Second) -> Self { | ||
ChainedConnector(first, second, PhantomData) | ||
} | ||
} | ||
|
||
impl<In, First, Second> fmt::Debug for ChainedConnector<In, First, Second> | ||
where | ||
In: Transport, | ||
First: Connector<In>, | ||
Second: Connector<First::Out>, | ||
{ | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_tuple("ChainedConnector") | ||
.field(&self.0) | ||
.field(&self.1) | ||
.finish() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way would be to implement this for tuples of sizes 0 to 8 through a macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean as an alternative syntax to .chain().chain().chain()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I don't know how much extra it aids clarity though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This avoids ugly ChainedConnector<First, ChainedConnnector<Second, ChainedConnector<...,...>>>
types and would instead allow:
ChainedConnector<(First, Second, Third, ...)>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing a bit the DefaultConnector
code to create a type error leads to this. The full type is ChainedConnector<(), ChainedConnector<(), ChainedConnector<(), ChainedConnector<(), ChainedConnector<(), ChainedConnector<(), (), WarnOnNoSocksConnector>, TcpConnector>, RustlsConnector>, WarnOnMissingTlsProvider>, WarnOnMissingTlsProvider>, ConnectProxyConnector>
It also means that DefaultConnector
boxes the inner
instead of having a more explicit type (and self-documenting)
#[derive(Debug)]
pub struct DefaultConnector {
inner: ChainedConnector<(), (
#[cfg(feature = "_test")]
test::TestConnector,
#[cfg(feature = "socks-proxy")]
SocksConnector,
#[cfg(feature = "socks-proxy")]
WarnOnNoSocksConnector,
TcpConnector,
#[cfg(feature = "rustls")]
RustlsConnector,
#[cfg(feature = "native-tls")]
NativeTlsConnector,
#[cfg(feature = "_tls")]
WarnOnMissingTlsProvider,
)>,
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that just the nature of combinators in Rust? Like every single thing on an Iterator
produces some intermediary type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterators are very rarely stored and named. And given the combinators used with iterator include closures, you wouldn't be able to name them anyways. Here the we combine named types, and there are only two types of combinators: Chaining
and Either
. I'm not suggesting doing this for Either
because it's much more annoying to generate many enum types. Chaining
is pretty well suited to being made easy to name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just using Iterator as example. Another is something like warp https://crates.io/crates/warp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More recently, xilem, axum and leptos do what I'm thinking about, implementing traits on many tuple length through a macro.
I don't think this is something very important. It's still possible to build combinators in a downstream crate.
Thank you! I'll try find the time to see if this fits our use case this month. |
Close #890