Here is a kind of lengthy example because I was not able to reduce it further. Rust Playground
use std::marker::PhantomData;
use std::ops::{Add, Sub, Mul, Di
ScalarVal(1) + a
resolves basically to <ScalarVal(1) as Add>.add(a)
, which looks for an Add
implementation on ScalarVal
.
For whatever reason, this one is checked first:
impl<PixelP> Add<Image<PixelP>> for ScalarVal<<PixelP as Pixel>::ScalarType>
where PixelP: Pixel,
ScalarVal<<PixelP as Pixel>::ScalarType>: Add<PixelP, Output = PixelP>
PixelP
is uninstantiated at this point, so PixelP: Pixel
can't be checked. Thus we get to
ScalarVal<<PixelP as Pixel>::ScalarType>: Add<PixelP, Output = PixelP>
Let's simplify this. Since PixelP
is unknown right now, <PixelP as Pixel>::ScalarType
is unknown. The actual information known by the compiler looks more like
impl<T> Add<Image<T>> for ScalarVal<_>
where ScalarVal<_>: Add<T, Output = T>
So ScalarVal<_>
looks for an Add<T, Output = T>
. This means we should look for an appropriate T
. Obviously this means looking in ScalarVal
's Add
impl
s. Looking at the same one, we get
impl<T2> Add<Image<T2>> for ScalarVal<_>
where ScalarVal<_>: Add<T2, Output = T2>
which means that if this one matches, T == Image<T2>
.
Obviously then T2 == Image<T3>
, T3 == Image<T4>
, etc. This results in an overflow and general sadness. Rust never finds a disproof, so can't ever guarantee it's going down the wrong path.