paholg / typenum

Compile time numbers in Rust.
https://crates.io/crates/typenum
Other
513 stars 45 forks source link

Is there a way to perform a 3 way OR in a trait bound? #206

Closed seftontycho closed 6 months ago

seftontycho commented 6 months ago

I wish to implement a trait when 2 of the generics are either equal or at least one of them is equal to 1. Here A and B have the ArrayLength Trait from GenericArray which is a superset of Unsigned.

Something like:


impl<T, A: ArrayLength, B: ArrayLength> MyTrait<A, B> for T
where
    Or<A as IsEqual<B>, Or<A as IsEqual<U1>,  B as IsEqual<U1>>>:,
{
    ...
}```

I am trying to migrate my code from using `generic_const_exprs` which allows this behaviour but is unstable/buggy.
paholg commented 6 months ago

That should definitely be doable. IsEqual returns a Bit, and BitOr is implemented on bits. Let me know if a concrete example would be helpful.

paholg commented 6 months ago

Here's the full example:

use std::ops::BitOr;

use generic_array::ArrayLength;
use typenum::{Eq, IsEqual, Or, Same, True, U1, U2, U3, U7};

pub trait MyTrait<A, B> {
    fn foo();
}

impl<T, A: ArrayLength, B: ArrayLength> MyTrait<A, B> for T
where
    A: IsEqual<B> + IsEqual<U1>,
    B: IsEqual<U1>,
    Eq<A, U1>: BitOr<Eq<B, U1>>,
    Eq<A, B>: BitOr<Or<Eq<A, U1>, Eq<B, U1>>>,
    Or<Eq<A, B>, Or<Eq<A, U1>, Eq<B, U1>>>: Same<True>,
{
    fn foo() {}
}

fn main() {
    <() as MyTrait<U1, U2>>::foo();
    <() as MyTrait<U7, U7>>::foo();
    <() as MyTrait<U3, U1>>::foo();
    // Fails to compile:
    // <() as MyTrait<U3, U2>>::foo();
}

Note the use of Same; this trait exists for exactly this purpose, to check type equality in things like where clauses.

seftontycho commented 6 months ago

Perfect that helps a lot!

seftontycho commented 6 months ago

That appears to have solved all my issues.