Rust type inference failing with const generic

I am running across a failure in Rust's type inference that I can't get my head around - as most of these cases go, I don't know whether this is a compiler bug / insufficiency, or the types shouldn't be inferred and I just don't understand why. Unfortunately, a minimum working example is going to take a bit of effort.

Our fundamental goal is to implement capability that dispatches to the num package in order to do math over vectors with custom types.

We begin with three generic traits: Producer, AbsSub, and Power.

use num_traits::{Pow, Signed};

pub trait Producer<B, const LEVEL: bool> {
    /// Produce a single element
    fn produce(&self) -> B;
}

pub trait AbsSub<A, B, const LEVEL: bool> {
    // Vec isn't important here, we just need a generic return type
    fn abs_sub(&self, other: &B) -> Vec<A>
    where
        A: Signed,
        B: Producer<A, LEVEL>;
}

pub trait Power<A, B, C, const LEVEL: bool> {
    fn power(&self, other: &C) -> Vec<A::Output>
    where
        A: Pow<B> + Copy,
        C: Producer<B, LEVEL>;
}

Of course, any type can produce itself, so we'll go ahead and add a blanket implementation:

impl<A> Producer<A, false> for A // Note the `false` here
where
    A: Copy,
{
    fn produce(&self) -> A {
        *self
    }
}

Now, we'll define a wrapper type, Wrapper, that we also want to produce. If we'd defined Producer without our const bool we'd be out of luck: our blanket implementation would mean we'd implemented Producer twice for the same type. Instead, we'll implement the true version of Producer for Wrapper:

#[derive(Debug, Clone, Copy)]
struct Wrapper<A>(A);

impl<A> Producer<A, true> for Wrapper<A>
where
    A: Copy,
{
    fn produce(&self) -> A {
        self.0
    }
}

Finally, we'll add implementations of AbsSub and Power and a main:

impl<A, B, const LEVEL: bool> AbsSub<A, B, LEVEL> for Vec<A> {
    fn abs_sub(&self, other: &B) -> Vec<A>
    where
        A: Signed,
        B: Producer<A, LEVEL>,
    {
        self.iter().map(|v| v.abs_sub(&other.produce())).collect()
    }
}

impl<A, B, C, const LEVEL: bool> Power<A, B, C, LEVEL> for Vec<A>
where
    C: Producer<B, LEVEL>,
{
    fn power(&self, other: &C) -> Vec<<A>::Output>
    where
        A: Pow<B> + Copy,
    {
        self.iter().map(|v| v.pow(other.produce())).collect()
    }
}

fn main() {
    let vec = vec![1, 2, 3];

    // `AbsSub` compiles and works
    let wrapped = Wrapper { 0: 2 };
    let result = vec.abs_sub(&2);
    assert_eq!(result, vec![0, 0, 1]);
    let result = vec.abs_sub(&wrapped);
    assert_eq!(result, vec![0, 0, 1]);

    let wrapped = Wrapper { 0: 2u8 };
    let result = vec.power(&2u8); // This compiles
    let result = vec.power(&wrapped); // This doesn't
    let result = Power::<_, _, _, true>::power(&vec, &wrapped); // But this does
}

It's incredible to me that any of this works, frankly. But I don't understand why inference works for the wrapped abs_sub, but not for the wrapped power. I get a ton of compiler errors:

error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
   = note: cannot satisfy `<i32 as Pow<_>>::Output == _`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: required by a const generic parameter in `Power::power`
  --> src/main.rs:7:26
   |
7  | pub trait Power<A, B, C, const LEVEL: bool> {
   |                          ^^^^^^^^^^^^^^^^^ required by this const generic parameter in `Power::power`
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0283]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
   = note: multiple `impl`s satisfying `i32: Pow<_>` found in the `num_traits` crate:
           - impl Pow<u16> for i32;
           - impl Pow<u32> for i32;
           - impl Pow<u8> for i32;
           - impl Pow<usize> for i32;
note: required by a bound in `Power::power`
  --> src/main.rs:10:12
   |
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
9  |     where
10 |         A: Pow<B> + Copy,
   |            ^^^^^^ required by this bound in `Power::power`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0283]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: multiple `impl`s satisfying `Wrapper<u8>: Producer<_, _>` found
  --> src/main.rs:24:1
   |
24 | / impl<A> Producer<A, false> for A
25 | | where
26 | |     A: Copy,
   | |____________^
...
33 | / impl<A> Producer<A, true> for Wrapper<A>
34 | | where
35 | |     A: Copy,
   | |____________^
note: required by a bound in `Power::power`
  --> src/main.rs:11:12
   |
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
...
11 |         C: Producer<B, LEVEL>;
   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `Power::power`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: required for `Vec<i32>` to implement `Power<i32, _, Wrapper<u8>, _>`
  --> src/main.rs:42:34
   |
42 | impl<A, B, C, const LEVEL: bool> Power<A, B, C, LEVEL> for Vec<A>
   |               -----------------  ^^^^^^^^^^^^^^^^^^^^^     ^^^^^^
   |               |
   |               unsatisfied trait bound introduced here
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~

If anyone can even provide any explanation for this behavior, that would be awesome. I'm doubtful that there is a "fix", but I'd really like to know whether this is a compiler insufficiency or a true inference ambiguity.

This happens because the compiler is unable to infer (because it is ambiguous) which impl Producer for Wrapper should be used.

Wrapper<u8> implements Producer<Wrapper<u8>, false> by the blanket impl of impl<A: Copy> Producer<A, false> for A.
However, Wrapper<u8> also implements Producer<Wrapper<u8>, true> because of the implementation impl<A: Copy> Producer<A, true> for Wrapper<A>.

The impl Power for Vec ends up having a bound Wrapper<u8>: Producer<B, LEVEL> where LEVEL is a constant bool generic. With no other bounds on B or LEVEL from the concrete use site <Vec<u8> as Power<u8, _, Wrapper<u8>, _>>, type inference fails.

(rustc will not work “backwards” from the two constraints Wrapper<u8>: Producer<B, LEVEL> and u8: Pow<B> to select B = u8 and LEVEL = true. What would happen if someone wrote impl Pow<Wrapper<u8>> for Wrapper<u8>?[1])

Inference succeeds on AbsSub because your call site is this: <Vec<u8> as AbsSub<u8, Wrapper<u8>, _>> and the compiler can determine that LEVEL = true from the bound Wrapper<u8>: Producer<u8, LEVEL>.

(To anyone else looking to answer this, I compiled Rust Playground from all of OP’s snippets).


  1. Then B = Wrapper<u8> and LEVEL = false would also be a valid solution and inference would have to fail. It fails in your current situation due to forwards compatibility IIRC (“single-impl inference” was what caused the breakage in the time crate in Rust 1.80). Also allowing your code to compile would be fragile and add a lot of code to the trait solver. ↩︎

3 Likes

This was a really excellent explanation, thank you! I really loved that you included the note about why rustc doesn't work backwards.

As a reasonable workaround, I can alter the Power trait definition to move B from a trait-level generic to a function-level generic. That way, users can specify vec.power::<u8>(&wrapped) rather than the entire Power::<_, _, _, true>::power(&vec, &wrapped) call.