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.
Lets think like a compiler when deducing
vec.power(&wrapped);
Skipping past most of method lookup, we know we're trying to match the Power
trait on Vec<i32>
with Wrapper<u8>
(those deductions are at least
available in the error output).
A
is i32
C
is Wrapper<u8>
What is B
? The only things to work from are the constraints:
C: Producer<B, LEVEL>
andA: Pow<B>
Unfortunately, neither constraint can deduce a singular B
based on A
and
C
. Both are "open sets": a C
can implement multiple Producer
s and an
A
can implement multiple Pow
s.
There definitely are multiple implementations of Pow
for i32
- the
error output shows this. So that is definitely not helpful.
And then there are two implementations of Producer
for Wrapper<u8>
: one
blanket implementation with LEVEL=false
and one generic implementation on
Wrapper<A>
with LEVEL=true
. Unfortunately for you, in the face of any
ambiguity, the compiler gives up and requires you to clarify. It does not
make any exploratory deductions based LEVEL
to see that one doesn't work.
Now when you specify <_, _, _, true>
, that does give it enough help to rule
our the blanket implementation such that only one implementation remains and
thus B
can be deduced.