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> {
    /* ... */
}

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!