I am developing an interface that can create connections using TCPStream or SerialPort from this library.
I want to have a member of a struct that implements the traits std::io::Read
and std::io::Write
, which both of these connections provide.
SerialPort
has trait SerialPort
which is Send + std::io::Read + std::io::Write
link
I have came up with this code:
trait ReadWriteIo: Read + Write {}
impl ReadWriteIo for TcpStream {}
impl ReadWriteIo for Box<dyn SerialPort> {}
struct ServerInterface {
connection: Box<dyn ReadWriteIo>,
// some other members
}
impl ServerInterface {
fn new(use_tcp: bool) -> Result<ServerInterface> {
let parser = MspParser::new();
if use_tcp {
let connection = TcpStream::connect("example:8080")?;
return Ok(ServerInterface {
connection: Box::new(connection),
// other members
});
}
// creates Box<dyn SerialPort>
let connection = serialport::new("/dev/ttyUSB0", DEFAULT_BAUD_RATE)
.timeout(time::Duration::from_millis(10))
.open()?;
Ok(ServerInterface {
// this gives error
// rustc: mismatched types
// expected struct `Box<(dyn ReadWriteIo + 'static)>`
// found struct `Box<dyn SerialPort>`
connection,
})
}
}
From what I understand, my issue is that the compiler does not want to accept that Box<dyn SerialPort>
can be Box<dyn ReadWriteIo>
, which is essentially its subtrait.
Is it possible to achieve this? Or maybe should I switch to an enum? Using traits would be nicer for me as I am only using Read
and Write
, so no other changes in the code are necessary.
The easiest way will be to wrap the Box<dyn SerialPort>
(that impls ReadWriteIo
with Box
and convert that to Box<dyn ReadWriteIo>
:
trait ReadWriteIo: Read + Write {}
impl ReadWriteIo for TcpStream {}
impl ReadWriteIo for Box<dyn SerialPort> {}
struct ServerInterface {
connection: Box<dyn ReadWriteIo>,
// some other members
}
impl ServerInterface {
fn new(use_tcp: bool) -> Result<ServerInterface> {
let parser = MspParser::new();
if use_tcp {
let connection = TcpStream::connect("example:8080")?;
return Ok(ServerInterface {
connection: Box::new(connection),
// other members
});
}
// creates Box<dyn SerialPort>
let connection = serialport::new("/dev/ttyUSB0", DEFAULT_BAUD_RATE)
.timeout(time::Duration::from_millis(10))
.open()?;
Ok(ServerInterface {
connection: Box::new(connection),
})
}
}
This will be slightly less efficient since it will involve double allocation, indirection and dynamic dispatch, but it should be fine most of the times.