I'm implementing a small service that accepts commands over TCP and relays it to device that also accepts commands over TCP.
I wrote the entire thing then went to test it and thought that the best way would be to use dependency injection so that I can provide some stream representing the device to test against that.
I am having trouble annotating the struct member that produces the stream.
How should I go about annotating a struct member that is a function that returns a type that implements Read
and Write
?
This is a rough sketch of the situation.
struct Handle {
// I want to describe a function that returns a object that implements Read and Write
stream_factory: Result<Box<dyn Read + Write>, Box<dyn std::error::Error>>
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let tcp_factory = || std::net::TcpStream::connect("127.0.0.1:1");
let handle_a = Handle {
stream_factory: tcp_factory
}
let file_factory = || std::file::File::options()
.read(true)
.write(true)
.open("foo.txt");
let handle_b = Handle {
stream_factory: tcp_factory
}
Ok(())
}
You can either box the a trait that has Read
and Write
as a supertrait
use std::io::{Read, Write};
use std::net::TcpStream;
trait ReadWrite: Read + Write {}
impl ReadWrite for TcpStream {}
struct Handle {
// You will need to use Box<Fn() -> ...>
// if you want to capture data in the factory function
stream_factory: fn() -> Result<Box<dyn ReadWrite>, std::io::Error>,
}
fn main() {
let tcp_factory = || {
TcpStream::connect("127.0.0.1:1")
.map(|x| Box::new(x) as Box<dyn ReadWrite>)
};
let handle_a = Handle {
stream_factory: tcp_factory,
};
}
Or you could use generics, though this may not work in your case if you need to pass different Read+Write
types to the same place.
struct Handle<T>
where T : Read + Write {
stream_factory: fn() -> Result<T, std::io::Error>,
}
fn main() {
let tcp_factory = || {
TcpStream::connect("127.0.0.1:1")
};
let handle_a = Handle {
stream_factory: tcp_factory,
};
}