I'm trying to an create a generic struct which wraps an isize or an AtomicIsize, but I am running into an error when I try to implement a trait for both possible implementations of the struct. I created a minimal example which demonstrates my issue below.
use std::sync::atomic::{AtomicIsize, Ordering};
use std::ops::Deref;
use std::marker::PhantomData;
pub trait Counted {
fn inc(&self, value: isize);
}
pub type PlainCounter = isize;
pub type AtomicCounter = AtomicIsize;
pub struct Counter<'a, T: 'a> {
counter: T,
phantom: PhantomData<&'a T>,
}
impl<'a, T> Counter<'a, T>
where T: Deref<Target = PlainCounter>
{
pub fn new(counter: T) -> Self {
Counter {
counter: counter,
phantom: PhantomData,
}
}
}
impl<'a, T> Counted for Counter<'a, T>
where T: Deref<Target = PlainCounter>
{
fn inc(&self, value: isize) {
self.counter += 1;
}
}
impl<'a, T> Counter<'a, T>
where T: Deref<Target = AtomicCounter>
{
pub fn new(counter: T) -> Self {
Counter {
counter: counter,
phantom: PhantomData,
}
}
}
impl<'a, T> Counted for Counter<'a, T>
where T: Deref<Target = AtomicCounter>
{
fn inc(&self, value: isize) {
self.counter.fetch_add(value, Ordering::SeqCst);
}
}
The error I get is that the compiler found conflicting implementations of trait `Counted` for type `Counter<'_, _>`. It seems that the compiler cannot determine that the implementations are for two different types T, namely T: Deref<Target = PlainCounter> and T: Deref<Target = AtomicCounter>. Is there perhaps a way to provide additional information to the compiler so it can distinguish between the two cases, or am I on the wrong path entirely?
You can accomplish this pattern by defining a second trait that does the actual work, and is implemented for (Counter<'a, T>, <T as Deref>::Target), and have the Counter trait call out to that implementation.
I don't think that was very clear, but I think an example can illustrate well. Using Shepmaster's shorter example for clarity, we would go from this:
use std::ops::Deref;
trait Foo {}
impl<T> Foo for T
where T: Deref<Target = u8>
{}
impl<T> Foo for T
where T: Deref<Target = bool>
{}
fn main() {}
to this:
use std::ops::Deref;
trait Foo {}
trait InnerFoo {}
impl<T> Foo for T
where T: Deref,
(T, <T as Deref>::Target): InnerFoo
{}
impl<T> InnerFoo for (T, u8)
{}
impl<T> InnerFoo for (T, bool)
{}
fn main() {}