I have a trait in my program that does something with a Read based on some settings stored in the concrete implementation, that (really for reasons of convenience, but its needed to avoid huge PITA) needs to be able to export its settings into another related struct, and that struct needs to be able to turn itself back into the struct that actually does things, just with a different type of Read. The behavior of the struct should be identical, it should not care what type of read it is using (so really, the related settings struct isn't needed, but its gotten me a lot closer than I was before).
I've tried giving up and just Boxing the Read, but that resulted in lifetime hell, and I'd much rather trade that for type hell.
I've boiled it down to this example. It compiles fine with the "more_indirection" fn removed, but not with it in place. The more_indirection fn in the actual code is burred in the default implementation for another trait, so I'm not sure its possible to pass through the concrete type of the UsesReader without major change to the code.
With the function there, I get a nice:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:123:46
|
123 | let doer = holder.do_thing_with_settings(thing);
| ^^^^^ expected struct `std::io::Empty`, found type parameter
|
= note: expected type `std::io::Empty`
found type `impl Read`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
What can I do to make the code work with the corner I have backed myself into?
use std::io::{copy, empty, stdout, Empty, Read};
use std::marker::PhantomData;
pub trait UsesReader<R: Read>: Sized {
type Settings: UsesReaderSettings<R>;
fn do_something(&mut self);
fn get_settings(&self) -> Self::Settings;
fn to_placeholder(self) -> UsesPlaceHolder<R, Self> {
UsesPlaceHolder {
uses_reader: self,
marker: PhantomData,
}
}
}
pub trait UsesReaderSettings<R: Read> {
type Uses: UsesReader<R>;
fn to_uses(&self, reader: R) -> Self::Uses;
}
pub struct ThingDoer<R: Read> {
do_the_thing: bool,
reader: R,
}
pub struct UsesPlaceHolder<R: Read, T: UsesReader<R>> {
uses_reader: T,
marker: PhantomData<R>,
}
impl<R: Read, T: UsesReader<R>> UsesPlaceHolder<R, T> {
pub fn do_it(&mut self) {
self.uses_reader.do_something();
}
}
impl<R: Read> UsesReader<R> for ThingDoer<R> {
type Settings = ThingDoerSettings;
fn do_something(&mut self) {
if self.do_the_thing {
let mut out = stdout();
copy(&mut self.reader, &mut out).unwrap();
} else {
println!("Nope!")
}
}
fn get_settings(&self) -> Self::Settings {
ThingDoerSettings {
do_the_thing: self.do_the_thing,
}
}
}
pub struct ThingDoerSettings {
do_the_thing: bool,
}
impl<R: Read> UsesReaderSettings<R> for ThingDoerSettings {
type Uses = ThingDoer<R>;
fn to_uses(&self, reader: R) -> Self::Uses {
ThingDoer {
do_the_thing: self.do_the_thing,
reader,
}
}
}
fn main() {
let test_string = b"Hello World!";
let test_doer = ThingDoer {
do_the_thing: true,
reader: empty(),
};
// Works
let settings = test_doer.get_settings();
let mut new_doer = settings.to_uses(&test_string[..]);
new_doer.do_something();
// Also works
let holder = SettingsHolder { settings };
holder
.do_thing_with_settings(&test_string[..])
.do_with_extra_step();
// Does not work
// more_indirection(holder, &test_string[..]);
}
pub struct SettingsHolder<S: UsesReaderSettings<Empty>> {
settings: S,
}
impl<S: UsesReaderSettings<Empty>> SettingsHolder<S> {
pub fn new(settings: S) -> SettingsHolder<S> {
SettingsHolder { settings }
}
pub fn do_thing_with_settings<R>(&self, reader: R) -> ExtraStep<R, impl UsesReader<R>>
where
R: Read,
S: UsesReaderSettings<R>,
{
let doer = <S as UsesReaderSettings<R>>::to_uses(&self.settings, reader).to_placeholder();
ExtraStep {
uses_placeholder: doer,
}
}
}
pub struct ExtraStep<R: Read, S: UsesReader<R>> {
uses_placeholder: UsesPlaceHolder<R, S>,
}
impl<R: Read, S: UsesReader<R>> ExtraStep<R, S> {
pub fn do_with_extra_step(&mut self) {
println!("An extra step!");
self.uses_placeholder.do_it();
}
}
fn more_indirection(holder: SettingsHolder<impl UsesReaderSettings<Empty>>, thing: impl Read) {
let doer = holder.do_thing_with_settings(thing);
doer.do_with_extra_step();
}
Thank you in advance.