Trait "transmutation" working when I know the concrete type, but not when I only know that it implements the trait

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();
}

(Playground)

Thank you in advance.

fn more_indirection(holder: SettingsHolder<impl UsesReaderSettings<Empty>>, thing: impl Read) {
   let doer = holder.do_thing_with_settings(thing);

This is an error because the type of holder only implements UsesReaderSettings<Empty> and doesn't implement UsesReaderSettings<R> where R is the type of thing: impl R. You have to declare every needed trait bounds:

fn more_indirection<R, S>(holder: SettingsHolder<S>, thing: R)
    where R: Read, S: UsesReaderSettings<Empty> + UsesReaderSettings<R> {

, or equivalently,

fn more_indirection<R: Read>(holder: SettingsHolder<impl UsesReaderSettings<Empty> + UsesReaderSettings<R>>, thing: R) { ...

Thank you!

I had a sneaking suspicion I was missing something trivial like that. That's what I get for letting myself bang my head against the wall for hours without a break I guess.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.