I'm translating a C++ codebase that makes heavy use of interface classes to Rust. The interfaces abstract away implementation detail of network connections. I tried using generics and bound traits, but the generic type definitions have begun to take over my whole codebase! How do I 'stop' the spread of generic type definitions?
Because this is supposed to be a drop-in replacement and because I think the C++ solution is quite elegant I'm trying to stay close to the original implementation. I thought I could use traits to implement interface like functionality.
For example, the C++ codebase does something along these lines:
class iConnection
{
virtual bool send_data(const std::vector<uint8_t>& bytes) = 0;
virtual std::vector<uint8_t> receive_data() = 0;
}
class tcp_connection : iConnection
{..} // handle tcp connection implementation details
class http_connection : iConnection
{..} // handle http connection implementation details
class Multiplexer
{
std::vector<std::shared_ptr<iConnection>> connections;
...
void send_data(const std::vector<uint8_t>& bytes)
{
for(auto& ptr: connections)
{
if( ptr->send_data(bytes) == true )
break;
}
}
}
Note how the 'generic/trait' definition is only present in the definition of the connections member of the Multiplexer class.
I've tried creating a similar structure in Rust. The code I have now is something along these lines:
pub trait iConnection {
fn send_data(&mut self, bytes: Vec<u8>) -> Result<(), ()>;
}
impl iConnection for TcpStream {
fn send_data(&mut self, bytes: Vec<u8>) -> Result<(), ()> {
self.Write(&bytes[..]);
}
}
impl iConnection for MyHttpClass {
fn send_data(&mut self, bytes: Vec<u8>) -> Result<(), ()> {
self.send_request(bytes);
}
}
pub struct Multiplexer<T: iConnection> {
pub connections: Vec<Box<T>>,
}
// this seems like a potential place to stop the spread.
// If there's a way for the multiplexer not to be generic
// That'd solve my problem.
impl<T: iConnection> send_data for Multiplexer<T> {
// use the connections
}
// ...
#[no_mangle]
pub extern "C" fn network_router_create<T: iConnection>() -> *mut Multiplexer<T> {
Box::into_raw(Box::new(Multiplexer::new()))
}
This would be fine if I didn't need to interface with this library from the existing C++ codebase (to use it as a drop-in replacement). For obvious reasons an exported, unmangled function cannot be generic, so my question is: how do I stop the spread of 'genericness' without having to duplicate code that deals with a connection object's implementation details?