Suppose this:
pub fn handle_connection(&mut data: impl NetworkStream) {...}
where NetworkStream
:
pub trait NetworkStream
So, I want to write, inside handle_connection
, some generic code that is valid for dispatch an HttpServer
, an UdpServer
or a WebsocketServer
custom types. The idea it's that the caller of handle_connection
could pass any type that implements some trait (like NetworkStream, for example), implemented for those types, and handle_connection
would be able to recognize, instanciate and call the correct type without knowing the incoming concrete type.
But, the main issue here is, how in Rust that piece of code is able to work with the impl NetworkStream
argument and translate it to concrete code?
As note, I am asking for the idiomatic way of make a type casting based on some condition, like Java's is instance of
operator, or for the proper way to do this kind of logic even if it's completly different of type checking and casting.
You can't differentiate inside of handle_connection
which type it actually is. You have to move any type-specific code into the type-specific implementations of the trait.
Also, minor nitpick: &mut data: impl NetworkStream
is a weird signature, it should probably be data: &mut impl Networkstream
.
This is how such an architecture could look like. Of course all the types are dummy mocks and should be replaced with actual types. It's just to demonstrate how this would look like:
struct HttpServer {}
struct UdpServer {}
struct WebsocketServer {}
trait NetworkStream {
fn receive_message(&mut self) -> String;
}
impl NetworkStream for HttpServer {
fn receive_message(&mut self) -> String {
"HttpMessage!".to_string()
}
}
impl NetworkStream for UdpServer {
fn receive_message(&mut self) -> String {
"UdpMessage!".to_string()
}
}
impl NetworkStream for WebsocketServer {
fn receive_message(&mut self) -> String {
"WebsocketMessage!".to_string()
}
}
fn handle_connection(data: &mut impl NetworkStream) {
let message = data.receive_message();
println!("Received: {:?}", message);
}
fn main() {
let mut http_server = HttpServer {};
let mut udp_server = UdpServer {};
let mut websocket_server = WebsocketServer {};
handle_connection(&mut http_server);
handle_connection(&mut udp_server);
handle_connection(&mut websocket_server);
}
Received: "HttpMessage!"
Received: "UdpMessage!"
Received: "WebsocketMessage!"
To answer the second part of your question, there is dynamic downcasting with std::any::Any
. But unless it's absolutely necessary, I would try to find a code structure that does not require Any
.
Usually, there is ways to resolve types statically, for example with the visitor pattern.