Not sure I agree with "relatively clean code" when one of the examples show "fn compose <T>(f1: impl Fn(T)->T, f2: impl Fn(T)->T) -> impl Fn(T)->T {" which is just a mix-match of keywords and other things, with tons of syntax embedded in just one line.
But as always, depends on where you come from. I mostly deal with lisp languages nowadays, so guessing it's just my view that the line quoted above seems complex enough to not be interested one bit in Rust.
That line is extremely simple to me. What makes it seem like a "mix-match" of symbols to you?
The equivalent in Haskell would be "compose :: (a -> a) -> (a -> a) -> a -> a", which lacks "impl" because it boxes all closures (each closure in Rust has its own type), and lacks "Fn" because it doesn't care about what kind of access the closure needs to its context (Rust distinguishes between Fn, FnMut and FnOnce). Still, the Rust is only a tiny bit more complicated than the Haskell.
Perhaps you should pick your examples more carefully. That function just repeats impl Fn(T)->T three times which is exactly what yo would expect from the compose function. If there was a mix of multiple different types of functions you could demonstrate the additional complexity of Rust. What you did is just prove that Rust is the same as most languages.
fn compose<T>(
f1: impl Fn(T)->T,
f2: impl Fn(T)->T
) -> impl Fn(T)->T
{
// both arguments as well as the returned value
// are functions which take and return the generic type T.
// (to be precise, they implement the Fn trait).
}
let returned_function = compose(|val| val == 1, |val| val < 0);
let value: bool = returned_function(true);
Lambdas have unique types, and you can't use a generic parameter in a return type (which is impl Trait's raison d'etre), so I think it would have to be this:
Yes, I think he meant that it would be a bad idea to lock all of the type parameters to the same type, because all closures have different types. So you wouldn't be able to do something like this
Because the parameters demand the same type (and for the return value), but the supplied parameters are not the same type because the only way to get that is to pass in exactly the same instance of the closure.
It's a fun little thing, and a good example of why impl Trait in argument position is a really nice addition to Rust, even though based on what we were originally excited about (impl Trait in return position for returning Iterators and other such things), the argument position form didn't seem so important.
While that may be an extreme example I agree. nothing clean about that code. I've only just begun to dive into rust, but I feel like I'd need a concordance to navigate the meaning of that snippet alone.
Bt then again, maybe it depends on where you come from.
But as always, depends on where you come from. I mostly deal with lisp languages nowadays, so guessing it's just my view that the line quoted above seems complex enough to not be interested one bit in Rust.