Working around specialization/mutually exclusive traits

I have two traits:

trait Read {
    fn read(reader: Reader) -> Self;
}
trait ReadWithData {
    fn read(data: GlobalData, reader: Reader) -> Self;
}

I use them for reading my state from a file. I can't just implement ReadWithData for everything because I also need to use this system for reading the data itself.

Rust lacks specialization or mutually exclusive traits so it's really difficult to work with this: There is no way to implement Read and ReadWithData correctly for tuples, since there can be overlap. Here's the problem:

  • I blanket impl ReadWithData for T: Read
  • I impl ReadWithData for (T, U) where T: ReadWithData, U: ReadWithData
  • Now I need to impl Read for (T, U) where T: Read, U: Read, but I can't do that because it produces a ReadWithData impl that overlaps with my previous impl

Is there any way to solve this? I don't mind rearchitecting everything, it just needs to be solved.

Instead of a blanket implementation, can you get away with a default implementation and explicitly implementing the trait for each type?

trait Read {
    fn read(reader: Reader) -> Self;
}

trait ReadWithData: Read {
    fn read(data: GlobalData, reader: Reader) -> Self {
        <Self as Read>::read(reader)
    }
}

or

trait Read: ReadWithData {
    fn read(reader: Reader) -> Self {
        <Self as ReadWithData>::read(Default::default(), reader)
    }
}

trait ReadWithData: Read {
    fn read(data: GlobalData, reader: Reader) -> Self;
}

You might also be able to do something useful with a generic trait:

trait Read<Globals> {
    fn read(data: Globals, reader: Reader) -> Self;
}

impl<T> Read<T> for GlobalData { /* ... */ }
impl Read<GlobalData> for NeedsGlobals { /* ... */ }
impl<T,U,Globals> Read<Globals> for (T, U)
where T: Read<Globals>, U: Read<Globals> {
    /* ... */
}
1 Like

I can't have trait ReadWithData: Read because many types that can implement ReadWithData cannot implement Read as they need the data to read. Also, GlobalData: !Default so the second method won't work either. Your solution with making the trait generic seems to work though. Thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.