Const Generics: Expected overlapping impls error but code compiles

I'm working on an application that needs to use multi-dimensional arrays and created a Tensor<T> wrapper.

For convenience, I also wrote some convenience From impls that use const generics to convert from native fixed-length arrays to my wrapper.

impl<T, const N: usize> From<[T; N]> for Tensor<T> {
    fn from(array: [T; N]) -> Self { unimplemented!() }
}

impl<T, const WIDTH: usize, const HEIGHT: usize>
    From<[[T; WIDTH]; HEIGHT]> for Tensor<T>
{
    fn from(array: [[T; WIDTH]; HEIGHT]) -> Self { unimplemented!() }
}

impl<T, const WIDTH: usize, const HEIGHT: usize, const DEPTH: usize>
    From<[[[T; WIDTH]; HEIGHT]; DEPTH]> for Tensor<T>
{
    fn from(array: [[[T; WIDTH]; HEIGHT]; DEPTH]) -> Self { unimplemented!() }
}

(playground)

I'm a little confused that this compiles, though. For example all three implementations are equally applicable for an expression like [[[0.0; A]; B]; C].into():

  • The first impl is satisfied where N = C and T = [[f64; A]; B]
  • The second impl is satisfied where WIDTH=B, HEIGHT=C, and T = [f64; A]
    the third impl is satisfied where where WIDTH=B, HEIGHT=C, DEPTH=A, and T = f64`

I though the compiler would prevent me from writing such implementations because they overlap and I'm not using specialisation... Is there something I'm missing here?

They are distinguashable with the return types. The first impl returns Tensor<[[f64; A]; B]>, second impl returns Tensor<[f64; A]>, and third impl returns Tensor<f64>.

2 Likes