Typeclass, generics and bounds


#1

Recently I needed a factorial and I thought why not try and make that generic for fun. I have to admit I failed. I went as far as:

fn factorial<T>(n: T) -> T
    where T: Add<T, Output=T> + Mul<T, Output=T> + One + Step + Mul<<Range<T> as Iterator>::Item>,
          Range<T>: std::iter::Iterator {
    (T::one()..n + T::one()).product::<T>()
}

at which point the compiler was still spitting the following error:

error: type mismatch resolving `<T as core::ops::Mul<<core::ops::Range<T> as core::iter::Iterator>::Item>>::Output == T`:
 expected associated type,
    found type parameter

I generally do not fight much with the compiler, but trait bounds have been the exception. From time to time, they have a tendency to accumulate because a bound calls for another one, which in turn calls for two others, and it becomes next to impossible to find the right combination.
In this example in particular, the bounds needed for the code to work take three times as much space as the actual code. And the ratio coding time versus figuring out the bounds is even more impressive.

So my questions are:

  • If the compiler can reason about the missing bounds, could it not generate the bounds by itself, at least to some extent? I had the impression that the bounds where more interesting as documentation than an actual need for the compiler.
  • Wouldn’t Rust benefit from having a way to group bounds, in typeclasses of some sort? For example, regarding number types, I find myself missing things like Num or Integer when I compare to haskell (which I dabbled with recently). I feel like the generic and functional sides of rust could mix better than they do right now, and rust is lacking a way to express some kind of type hierarchy, and not just horizontal dependencies.

I’m not trying to pick on Rust, I love the language :slightly_smiling:, I’m just hoping to get explanations for some of its more frustrating sides.

Somewhat in an extension of my second question, is there a reason why I almost never can use sum() and product() without specifying the target type manually? The documentation example for product() is literally the only code for which I’ve been saved from doing sum::<u32>() by the type inference.


#2

Going back to template duck-typing is not a good idea in Rust. A better idea is for the Rustc compiler to give error messages that suggest all the missing traits.


#3

Well I’m not thinking of omitting them, just that they could be generated, albeit that might require some IDE and integration.