Mysterious conflicting implementations

Viewed 198

I was trying to test my code with fixed time. so I wrote something like this:

use std::time::{SystemTime, UNIX_EPOCH};

trait Clock {
    fn now(&self) -> SystemTime;
}

trait MixInClock {
    type Impl;
}

struct RealClock;
impl<T: MixInClock<Impl = RealClock>> Clock for T {
    fn now(&self) -> SystemTime {
        SystemTime::now()
    }
}

struct FakeClock;
impl <T: MixInClock<Impl = FakeClock>> Clock for T {
    fn now(&self) -> SystemTime {
        UNIX_EPOCH
    }
}

struct DIContainer;
impl MixInClock for DIContainer {
    type Impl = FakeClock;
}

This code gives me an error:


error[E0119]: conflicting implementations of trait `Clock`:
  --> src/lib.rs:19:1
   |
12 | impl<T: MixInClock<Impl = RealClock>> Clock for T {
   | ------------------------------------------------- first implementation here
...
19 | impl <T: MixInClock<Impl = FakeClock>> Clock for T {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

Why? There's no possibility that T implements MixInClock<Impl = RealClock> and MixInClock<Impl = FakeClock> at same time. right?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=95e3647b30ae0e37c45ac18da495a3c5

2 Answers

You ran into a long-standing issue with Rust's coherence rules. There are proposals how to resolve this, but the work has been postponed for now. Quoting Jonas Schievink's most recent comment on the bug:

To give an update on this: In 2016, RFC 1672 was proposed to fix this, but was postponed until the Chalk integration is done. Additionally, according to rust-lang/rfcs#1672 (comment), allowing these kinds of impls would allow users to express mutually exclusive traits, which is a very powerful feature that needs more in-depth consideration by the language team (hence marking as blocked on an RFC as well).

While Sven's answer is correct, that this is an issue with Rust's coherence rules, you can solve this problem by just generalizing your code a bit. In praticular, when you have two different types that should have different behavior but a similar interface, it's often useful to create a trait for that.

In this case, you could create a trait for RealClock and FakeClock that each can implement, and use that in your generic Clock implementation instead of adding the behavior of each directly:

// Common interface for RealClock and FakeClock, implemented separated for each
trait ClockImpl {
    fn now() -> SystemTime;
}

impl ClockImpl for RealClock {
    fn now() -> SystemTime {
        SystemTime::now()
    }
}

impl ClockImpl for FakeClock {
    fn now() -> SystemTime {
        UNIX_EPOCH
    }
}

// MixInClock only needs to now that Self::Impl implements ClockImpl
trait MixInClock {
    type Impl: ClockImpl;
}

// We call the T::Impl::now() method for the specific behavior of RealClock and FakeClock
impl<T: MixInClock> Clock for T {
    fn now(&self) -> SystemTime {
        <T::Impl as ClockImpl>::now()
    }
}

Code in playground

Related