Search code examples
genericsrustconstantsimplementation

Why can a method with a const generic boolean not call a method that is implemented for both true and false?


This code works perfectly (playground):

struct MyStruct<const B: bool>;

impl MyStruct<false> {
    pub fn bar() {
        println!("false");
    }
}
impl MyStruct<true> {
    pub fn bar() {
        println!("true");
    }
}

impl MyStruct<false> {
    pub fn foo() {
        MyStruct::<false>::bar()
    }
}
impl MyStruct<true> {
    pub fn foo() {
        MyStruct::<true>::bar()
    }
}

fn main() {
    MyStruct::<false>::foo();
    MyStruct::<true>::foo();
}

It results in:

false
true

On the other hand, this code will fail (playground):

struct MyStruct<const B: bool>;

impl MyStruct<false> {
    pub fn bar() {
        println!("false");
    }
}
impl MyStruct<true> {
    pub fn bar() {
        println!("true");
    }
}

impl<const B: bool> MyStruct<B> {
    pub fn foo() {
        MyStruct::<B>::bar()
    }
}

fn main() {
    MyStruct::<false>::foo();
    MyStruct::<true>::foo();
}

Resulting in:

error[E0599]: no function or associated item named `bar` found for struct `MyStruct<B>` in the current scope
  --> src/main.rs:16:24
   |
1  | struct MyStruct<const B: bool>;
   | ------------------------------- function or associated item `bar` not found for this
...
16 |         MyStruct::<B>::bar()
   |                        ^^^ function or associated item not found in `MyStruct<B>`
   |
   = note: the function or associated item was found for
           - `MyStruct<false>`
           - `MyStruct<true>`

I could understand this error in the case of infinitely valued types, but why for booleans?

Is there a way to overcome this?


Solution

  • One has to use a trait and generic type conditioning (playground):

    struct MyStruct<const B: bool>;
    
    trait Bar {
        fn bar();
    }
    
    impl Bar for MyStruct<false> {
        fn bar() {
            println!("false");
        }
    }
    impl Bar for MyStruct<true> {
        fn bar() {
            println!("true");
        }
    }
    
    impl<const B: bool> MyStruct<B>
    where
        MyStruct<B>: Bar,
    {
        pub fn foo() {
            MyStruct::<B>::bar()
        }
    }
    
    fn main() {
        MyStruct::<false>::foo();
        MyStruct::<true>::foo();
    }
    

    Of course, the principle is generalizable to other types than bool (playground):

    struct MyStruct<const U: usize>;
    
    trait Bar {
        fn bar();
    }
    
    impl Bar for MyStruct<0> {
        fn bar() { println!("0"); }
    }
    impl Bar for MyStruct<1> {
        fn bar() { println!("1"); }
    }
    impl Bar for MyStruct<100> {
        fn bar() { println!("100"); }
    }
    
    impl<const U: usize> MyStruct<U> where MyStruct<U> : Bar {
        pub fn foo() { MyStruct::<U>::bar() }
    }
    
    fn main() {
        MyStruct::<0>::foo();
        MyStruct::<1>::foo();
        MyStruct::<100>::foo();
    }