How to exclude a type from generic trait implementation?

I need to implement a trait for Vec<T> but Vec<u8> needs some special treatment. How can I handle this special case?

trait MyTrait { }

impl<T> MyTrait for Vec<T> /* where T: !u8 */ { }

impl MyTrait for Vec<u8> { }

results in

error[E0119]: conflicting implementations of trait `MyTrait` for type `std::vec::Vec<u8>`:

I guess that Tracking issue for specialization (RFC 1210) · Issue #31844 · rust-lang/rust · GitHub will allow this some day but I wonder if there is a workaround for my case readily available.

I tried to filter the types by TypeId::of::<T>() == TypeId::of::<u8> but this is restricted to 'static lifetimes only. I also tried to filter by mem::size_of == 1 but this gives my false positives on bool at least.

I don't mind to implement the trait for all built-in primitive types if that helps, but I don't want to implement this for every type.

4 Likes

This is not currently possible to do in a generic way; you should use a macro and write out all the impls you want.

1 Like

I just found a workaround that works for me:

pub trait MyTrait {
    fn is_special_case() -> bool { false }
}

This gets overridden with true for u8 only. Even though this is a run-time check, the compiler should optimize this away.

7 Likes

You may replace that with an associated const to elevate part of that check to compile time:

trait MyTrait {
    const IS_SPECIAL_CASE: bool = false;
}

impl MyTrait for Vec<u8> {
    const IS_SPECIAL_CASE: bool = true;
}

I expect the compiler to optimize the fn in the same way, but associated consts just seem like a better way.

9 Likes

Oh, I didn't know that consts can be overwritten. That's definitely better than a (const) method.
Thank you!

1 Like

Hi, I am new to rust and I cant figure out how to implement this.
for the following code:

trait MyTrait {
    const isu8: bool = false;
}

impl<T> MyTrait for [T] { }

impl MyTrait for [u8] {
    const isu8: bool = true;
}

I cant figure out how to get the value of isu8, I can do MyTrait::iu8 but that returns the constant true, self.isu8 is undefined(inside a method of the implementation). And how do you have to actually do the check? Just if self.isu8 and then the code for the u8 implementation?

PS sorry for the retracted post, I didnt know ctrl enter sends the post

Those two impls overlap, so it won't compile without specialization, which isn't stable yet.

Assuming you overcome that problem, in a function or struct that is generic over T: MyTrait, you can just use T::isu8.

Here's a simple example, renamed to silence compiler warnings about style:

trait MyTrait {
    const IS_U8: bool = false;
}

impl MyTrait for [u8] {
    const IS_U8: bool = true;
}

fn foo<T: MyTrait + ?Sized>(arg: &T) {
    if T::IS_U8 {
        // ...
    } else {
        // ...
    }
}
2 Likes

It's currently unstable but you could also use auto traits (the code below compiles on nightly):

#![feature(optin_builtin_traits)]

auto trait IsNotU8 {}

impl !IsNotU8 for u8 {}

trait MyTrait {
    fn is_u8_vec(&self) -> bool;
}

impl<T: IsNotU8> MyTrait for Vec<T> {
    fn is_u8_vec(&self) -> bool { false }
}

impl MyTrait for Vec<u8> {
    fn is_u8_vec(&self) -> bool { true }
}

fn main() {
    assert!(Vec::<u8>::new().is_u8_vec());
    assert!(!Vec::<u16>::new().is_u8_vec());
}

It's a bit awkward with the double-negative impl on u8. It would be made a lot nicer if negative trait bounds were allowed (impl<T: !IsU8> MyTrait for T or impl MyTrait for !u8) but AFAICT there's no way to get this running at the moment.

Similar behavior can be accomplished with the unstable trait specialization feature, but if that would work for you, you should take a look at case-studies/README.md at master · dtolnay/case-studies · GitHub