This is a problem that I think I'd know how to solve in e.g. OCaml, but I'm not sure on how to best phrase things to make the rust compiler happy. Which has me thinking that maybe I'm trying to code OCaml in rust and I should be going about it in a completely different way
I'm making use of a crate that'll (generically) process some elements for me:
mod external {
// This is an external "engine" that, given some elements of type T,
// will produce Thingies which reference the elements and their position
// in the &[T] passed in. We only care about the latter in this instance,
// but it's the former that's giving us trouble.
pub struct Thingy<T : PartialEq + Clone> {
pub backref : usize,
boring : T,
}
pub fn process_elements<T : PartialEq + Clone>(elems : &[T]) -> Vec<Thingy<T>> {
elems.iter().enumerate().map(|(i, el)| {
Thingy {
backref : i,
boring : el.clone(),
}
}).collect::<Vec<Thingy<T>>>()
}
}
Then, I'm doing some filtering, reordering and grouping of the thingies and sticking them in a
struct ThingyHolder<T : PartialEq + Clone> {
other_stuff : usize,
thingies : Vec<Thingy<T>>,
}
(this is required because the other_stuff
value depends on the eventual number of thingies
).
I have two concrete types in mind for T
(let's say A
and B
) and the formatting when I'm eventually printing them out needs to be different. In order to achieve that, I'm defining a trait and want to implement that only for ThingyHolder<A>
and ThingyHolder<B>
.
The trait looks like this:
trait DumpableHolder where Self::Item : PartialEq + Clone {
type Item;
fn do_write(&self, &[Self::Item], &mut Write);
}
and a mock implementation for A = Vec<u8>
would be:
impl DumpableHolder for ThingyHolder<Vec<u8>> {
type Item=Vec<u8>;
fn do_write(&self, elems : &[Self::Item], out: &mut Write) {
writeln!(out, "stuff {}", self.other_stuff);
for thingy in self.thingies {
// Referencing an elem is a hard requirement
out.write(&elems[thingy.backref]);
}
}
}
Now, I want to do my processing, creating ThingyHolder
s as I go along, but dump them out as soon as a ThingyHolder is complete. The relevant function is called process_thingies
and I've tried to make it generic in two different ways.
Version 1, parameterizes it over T
:
fn process_thingies<T>(out : &mut Write, elements : &[T], thingies : Vec<Thingy<T>>) -> io::Result<()>
where T : PartialEq + Clone,
ThingyHolder<T> : DumpableHolder
but of course this fails with the expected associated type, found type parameter
error message (see the playground link for the exact error message).
Version 2, tries to parameterize over ThingyHolder<T>
:
fn process_thingies<MyThingyHolder>(
out : &mut Write,
elements : &[<MyThingyHolder as DumpableHolder>::Item],
thingies : Vec<Thingy<<MyThingyHolder as DumpableHolder>::Item>>) -> io::Result<()>
where MyThingyHolder : DumpableHolder
but apparently, as the construction of the ThingyHolder
in the body of process_thingies
references ThingyHolder< <MyThingyHolder as DumpableHolder>::Item >
, it appears that the fact that MyThingyHolder
necessarily implements DumpableHolder
is lost on the compiler (again, see the playground version for the exact error).
At this point, however, I'm somewhat stuck as to how to work around this issue (limitation?). I've looked at https://stackoverflow.com/questions/29345708/matching-a-generic-parameter-to-an-associated-type-in-an-impl for inspiration, but the suggestion doesn't seem to be directly applicable here.
Any pointers? Please note that, despite my efforts, the linked playground versions might be easier to understand than the code excerpts above.