'Pick preferred implementation on conflicting trait implementation (using negative bounds)

I am working on a Rust program where I got stuck on a problem that can be reduced to following case:

struct Pair<L, R> {
  left: L,
  right: R,
}

// Returns the first `u32` in the pair (only defined for pairs containing an u32)
trait GetU32 {
  fn get(&self) -> u32;
}

// This should also be used for `Pair<u32, u32>`
impl<R> GetU32 for Pair<u32, R> {
  fn get(&self) -> u32 {
    self.left
  }
}

impl<L> GetU32 for Pair<L, u32> {
  fn get(&self) -> u32 {
    self.right
  }
}

// impl GetU32 for Pair<u32, u32> {
//   fn get(&self) -> u32 {
//     self.left
//   }
// }

fn main() {
  let a: Pair<u8, u32> = Pair {left: 0u8, right: 999u32};
  assert_eq!(999u32, a.get());

  let b: Pair<u32, u8> = Pair {left: 999u32, right: 0u8};
  assert_eq!(999u32, b.get());

  let c: Pair<u32, u32> = Pair {left: 999u32, right: 0u32};
  assert_eq!(999u32, c.get());
}

Playground link

I have a struct with two fields. If one (or both) of the fields are an u32, I want to return the first u32. The field to use should be picked statically during compilation.

The problem with the code above is that I can't express which implementation has a higher priority and it causes a conflict in the case Pair<u32, u32>.

error[E0119]: conflicting implementations of trait `GetU32` for type `Pair<u32, u32>`:
  --> crates/etwin_simple_user_pg/src/main.rs:20:1
   |
12 | impl<R> GetU32 for Pair<u32, R> {
   | ------------------------------- first implementation here
...
18 | default impl<L> GetU32 for Pair<L, u32> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Pair<u32, u32>`

How can I solve this conflict by picking the preferred implementation? I am OK with using nightly features such as specialization.

I looked into defining the conflicting case explicitly (commented code) but it only led to more conflicts. Another solution I pursued was trying to use specialization but I wasn't able to apply it to my use-case. Another solution would be to specify the second implementation with negative bounds as impl<L: !u32> GetU32 for Pair<L, u32> (only defined for traits where the only u32 is .right) but negative bounds don't exist.

I know that there are other questions on conflicting trait implementations, but I did not found a question where the conflict comes from being unable to pick the preferred implementation in such a simple case.


Edit I'd like to expand my question to give more context on my real problem and the solution I am using currently.

I am creating a structure similar to a frunk::HList to build an API object bit by bit by adding (or overriding) services. This struct remembers which services were registered and allows to retrieve them later. This all happens statically so the compiler can force the service to be registered and know which field corresponds to it. (Similarly to the minimal example above where the compiler should know that the pair has an u32 and in which field).

Since I cannot express a negative bound, I am currently implementing the secondary getter for every type from the negative set that I care about (see LotB's answer). This requires to manually update the implementations for this struct when I need new types. In the example above, it would correspond to the following code if my types were the unsigned integers:

impl<R> GetU32 for Pair<u32, R> {
  fn get(&self) -> u32 {
    self.left
  }
}

impl GetU32 for Pair<u8, u32> {
  fn get(&self) -> u32 { self.right }
}

impl GetU32 for Pair<u16, u32> {
  fn get(&self) -> u32 { self.right }
}

impl GetU32 for Pair<u64, u32> {
  fn get(&self) -> u32 { self.right }
}


Solution 1:[1]

As mentioned in the question, this situation can be solved using a negative bound. This feature is not available yet, even on the nightly branch.

Luckily, there is a workaround to achieve a sufficient form of negative bounds by combining two existing nightly features: auto_traits and negative_impls.

Here is the code:

#![feature(auto_traits, negative_impls)]

auto trait NotU32 {}

// Double negation: `u32` is not a "not `u32`"
impl !NotU32 for u32 {}

struct Pair<L, R> {
  left: L,
  right: R,
}

// Returns the first `u32` in the pair (only defined for pairs containing an u32)
trait GetU32 {
  fn get(&self) -> u32;
}

// This should also be used for `Pair<u32, u32>`
impl<R> GetU32 for Pair<u32, R> {
  fn get(&self) -> u32 {
    self.left
  }
}

impl<L: NotU32> GetU32 for Pair<L, u32> {
  fn get(&self) -> u32 {
    self.right
  }
}

fn main() {
  let a: Pair<u8, u32> = Pair {left: 0u8, right: 999u32};
  assert_eq!(999u32, dbg!(a.get()));

  let b: Pair<u32, u8> = Pair {left: 999u32, right: 0u8};
  assert_eq!(999u32, dbg!(b.get()));

  let c: Pair<u32, u32> = Pair {left: 999u32, right: 0u32};
  assert_eq!(999u32, dbg!(c.get()));
}

Playground link

Since we can't define the secondary getter with impl<L: !u32> GetU32 for Pair<L, u32> { ... } (negative bound), we instead define it using the marker trait NotU32 as impl<L: NotU32> GetU32 for Pair<L, u32> { ... }. This moves the issue: we now have to set this marker trait for all types except u32. This is were the auto_trait (add a trait for all types) and negative_impl (remove it from some types) come in.

The limit of this answer is that you can't define a generic Not<T> trait with this method today.

Solution 2:[2]

An easy way to avoid the problem would be to implement only the types you need, and not be generic. so instead of a generic

impl<R> GetU32 for Pair<u32, R>

you impl on the specific type, so you control each specific combination.

impl GetU32 for Pair<u32, u8>

A macro can help in defining the various impl instead of directly defining them, to make the process of specific definition easier if you have a lot of types and combinations to handle.

macro_rules! impl_pair {
    ( $left:ident, $right:ident, $side:ident) => {
        impl GetU32 for Pair<$left, $right> {
            fn get(&self) -> u32 {
                self.$side
            }
        }
    }
}

impl_pair!(u32, u8, left);
impl_pair!(u8, u32, right);
impl_pair!(u32, u32, left);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 LotB