Warning
This post was published 43 days ago. The information described in this article may have changed.
Hi!
I have the following somewhat convoluted code below. I tried doing a more minimal reproducible example for the playground but I can't hit it, unfortunately (attempt here). Apologies for that, truly.
I guess it would be easier to describe - I have a builder with two fields, both of which depend on a generic parameter and can be optional. I have generic methods in the builder that consume the builder and return a new builder from a different type, replacing one of the field types with another and keeping the other field. (Ignore simplification opportunities, but feedback welcome if you have it).
pub struct Builder<TCPFilter, TCPRouter, UDPHandler>
where
TCPFilter: crate::TCPFilter + Send + 'static,
TCPRouter: crate::TCPRouter + Send + 'static,
UDPHandler: crate::UDPHandler + Send + 'static,
{
tcp_stack: Option<TCPStack<TCPFilter, TCPRouter>>,
udp_stack: Option<UDPStack<UDPHandler>>,
}
impl<TCPFilter, TCPRouter, UDPHandler> Builder<TCPFilter, TCPRouter, UDPHandler>
where
TCPFilter: crate::TCPFilter + Send + 'static,
TCPRouter: crate::TCPRouter + Send + 'static,
UDPHandler: crate::UDPHandler + Send + 'static,
{
pub fn new() -> Self {
Self {
tcp_stack: None,
udp_stack: None,
}
}
// Keep the UDPHandler type, but replace the others
pub fn with_tcp_stack<H, R>(self, tcp_handler: H, tcp_router: R) -> Builder<H, R, UDPHandler>
where
H: crate::TCPFilter + Send + 'static,
R: crate::TCPRouter + Send + 'static,
{
return Builder {
tcp_stack: Some(TCPStack::new(tcp_handler, tcp_router)),
udp_stack: self.udp_stack,
};
}
// Keep the TCPFilter and Router types, but replace the UDPHandler
pub fn with_udp_stack<Handler>(
self,
udp_stack: Handler,
) -> Builder<TCPFilter, TCPRouter, Handler>
where
Handler: crate::UDPHandler + Send + 'static,
{
return Builder {
tcp_stack: self.tcp_stack,
udp_stack: Some(UDPStack::new(udp_stack)),
};
}
}
Using the builder works like this:
pub struct PassthroughUDP {}
impl UDPHandler for PassthroughUDP {...}
pub struct PassthroughTCP {}
impl TCPFilter for PassthroughTCP {...}
// Skipping TCPRouter to shorten
fn main() {
builder() // Creates a Builder with some "default" types
// Change the TCP-related types
.with_tcp_stack(
PassthroughTCP::new(),
MyTCPRouter::new()
)
// Change the UDP-related types
.with_udp_stack(PassthroughUDP::new())
.build();
}
This all compiles fine with the example Handler/Filter implementations above, which are different than the "default" types of the builder we get through builder()
.
The problem comes when I try to pass an instantiation of the following generic struct:
pub struct InspectTCP<S, D>
where
S: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
D: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
{
inspect_source: S,
inspect_destination: D,
}
impl<S, D> InspectTCP<S, D>
where
S: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
D: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
{
pub fn new(inspect_source: S, inspect_destination: D) -> Self {
Self {
inspect_source,
inspect_destination,
}
}
}
impl<S, D> TCPFilter for InspectTCP<S, D>
where
S: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
D: for<'a> FnMut(&'a [u8]) + Send + Clone + 'static,
{ ... }
// Use
fn main() {
let source_reader = |bytes_read| {
// whatever
};
let destination_reader = move |bytes_read| {
//whatever
};
builder()
.with_tcp_stack(
InspectTCP::new(source_reader, destination_reader),
MyTCPRouter::new()
)
.with_udp_stack(PassthroughUDP::new())
.build();
}
Error:
Error[E0599]: no method named `with_udp_stack` found for struct `Builder<InspectTCP<{closure@main.rs:205:25}, {closure@...}>, ..., ...>` in the current scope
--> src/main.rs:230:10
|
216 | builder()
| _______________-
217 | | ...
... |
230 | | .with_udp_stack(PassthroughUDP::new())
| | -^^^^^^^^^^^^^^ method not found in `Builder<InspectTCP<{closure@main.rs:205:25}, {closure@...}>, ..., ...>`
| |_________|
|
|
= note: the method was found for
- `Builder<TCPFilter, TCPRouter, UDPHandler>`
Based on what I gathered as research so far, I might be messing up the lifetime params of the reference inside the FnMut. But then again, why doesn't the compiler report type mismatch? I tried removing the for<'a>
bounds as well but with no luck. What am I missing? Could this be a compiler bug? I tested on both 1.86 and 1.87.
EDIT: If I change from FnMut(&[u8])
to FnMut(Vec<u8>)
(and the corresponding implementation to make that work), everything compiles fine. More evidence the ref lifetime is probably causing trouble here.
2 posts - 2 participants
🏷️ rust_feed