[SOLVED] Help with type inference (selecting array size with a turbofish)


#1

You know, it troubles me that, after a year of being on this forum (and certainly more than a year of using rust!), the best thread title I can come up with for my issue related to type inference is Help with type inference. (well, okay, I tried to add some sort of subtitle, but I’m sure it fails to identify the actual problem)


Following up on this thread, I decided to design a little utility api for working with views of contiguous data as slices of arrays.

My thoughts were:

  • I’d like to support vecs and slices with little hassle. An extension trait implemented on [T] is really good for this due to deref coercions. (This also supports method position, which is good for chaining.)
  • I need a way to select the array size when adding an array layer. Const generics are still not a thing, so I decided to try to support array size selection via turbofish: ::<[_; n]>.
  • The expected use case is for small arrays of known size where the input slice is already known to be divisible by that length. Panicking on bad inputs is fine. Limiting support up to n=32 is fine.

So here’s an example of code that I would like to work:

// here's a Vec<[[i32; 1]; 3]>
let vec = vec![[[0i32], [1], [2]], [[3], [4], [5]]];
// Go from &[[i32; 1]; 3] -> &[[i32; 1]] -> &[i32]
let slice = slice.flat().flat();
// Go from &[i32] -> &[[i32; 2]] -> &[[[i32; 2]; 1]
let slice = slice.nest::<[_; 2]>().nest::<[_; 1]>();
assert_eq!(slice, &[[[0i32, 1]], [[2, 3]], [[4, 5]]]);

Unfortunately, with my current implementation, I get this:

   Compiling playground v0.0.1 (file:///playground)
error[E0282]: type annotations needed
  --> src/main.rs:13:28
   |
13 |         let xs = xs.nest::<[_; 2]>().nest::<[_; 1]>();
   |                            ^^^^^^ cannot infer type for `_`

My first thought is to try to add some other trait with an associated type that would let me use some sort of equality bound (exact details TBD), but I also have the feeling that it’d probably be a waste of time, since I know that rustc is often pretty conservative when reasoning about type equalities (due to coherence concerns and possible implementations in other crates).

So my question is: Can I make this work?
(Specifically with regards to type inference in nest::<[_; n]>()?)
Or do I have to give up the turbofish?


Here’s my traits, and a compiling playground link.

pub trait ArrayExt { type Element; }
impl<T> ArrayExt for [T; 1] { type Element = T; }
impl<T> ArrayExt for [T; 2] { type Element = T; }
impl<T> ArrayExt for [T; 3] { type Element = T; }
// ...

pub trait SliceFlatExt<T> {
    fn flat(&self) -> &[T];
    fn flat_mut(&mut self) -> &mut [T];
}
impl<T, V> SliceFlatExt<T> for [V]
where V: ArrayExt<Element=T> { /* ... */ }

pub trait SliceNestExt {
    fn nest<V: ArrayExt>(&self) -> &[V];
    fn nest_mut<V: ArrayExt>(&mut self) -> &mut [V];
}
impl<T> SliceNestExt for [T] { /* ... */ }

Edit: Simplified the implementation slightly by implementing a trait on [T; n] instead of [[T; n]]
Edit 2: More cleanup; SliceFlatExt is now parameterized over the element T instead of the array V


#2

Hm.

Hmmmmm.

Hmmmmmmmmmm.

I wonder if my troubles could possibly have anything to do with the fact that, given the signatures I have written, all of the following are valid by the typechecker?

let vec = vec![1, 2, 3, 4, 5, 6];

let a = vec.nest::<[i32; 2]>(); // alright...
let a = vec.nest::<[i32; 3]>(); // cool...

let a = vec.nest::<[f32; 2]>(); // ...uh oh.
let a = vec.nest::<[(); 2]>();  // ohhh no.

enum Never {}
let a = vec.nest::<[Never; 2]>(); // no nonononononono

I somehow have a bad feeling that type inference still won’t work once I fix this glaring error, but hey, it’s certainly a place to start!


#3

So yeah, that was it. The only reason type inference wasn’t working was because I was giving it very good reason not to work… Everything is hunky dory now.

pub trait ArrayExt { type Element; }
impl<T> ArrayExt for [T; 1] { type Element = T; }
impl<T> ArrayExt for [T; 2] { type Element = T; }
impl<T> ArrayExt for [T; 3] { type Element = T; }
// ...

pub trait SliceFlatExt<T> {
    fn flat(&self) -> &[T];
    fn flat_mut(&mut self) -> &mut [T];
}
impl<T, V> SliceFlatExt<T> for [V]
where V: ArrayExt<Element=T> { /* ... */ }

// NOTE: T parameter and Element=T bounds added
pub trait SliceNestExt<T> {
    fn nest<V: ArrayExt<Element=T>>(&self) -> &[V];
    fn nest_mut<V: ArrayExt<Element=T>>(&mut self) -> &mut [V];
}
impl<T> SliceNestExt<T> for [T] { /* ... */ }

(amazing, I write one little bit of unsafe code and already I’m off accidentally casting *mut i32s into *mut Nevers, grumble grumble grumble)