Generic in argument _and_ return: impl trait vs generic

I searched quite a bit and couldn't find a similar question, so here we go:

I'm writing a function that has a generic in the argument as well as the return. I understand that when a generic is in the return, the caller must supply it. What I don't understand is why the caller seemingly must also provide the generic for the argument?

A dummy example below which takes anything that implements AsRef<str>, and attempts to parse it as an unsigned integer. Working playground

fn parse_as_int<N, S>(key: S) -> Result<N, ParseIntError>
    where
        N: FromStr<Err = ParseIntError> + Unsigned,
        S: AsRef<str>,
{
        let key = key.as_ref();
        key.parse()
}

The above function produces the error:

5  | fn parse_as_int<N, S>(key: S) -> Result<N, ParseIntError>
   |    ^^^^^^^^^^^^ -  -
help: add missing generic argument
   |
16 |     let _ = parse_as_int::<u8, _>("8");

The suggestion to use <u8, _> works but is fairly ugly and unfriendly from the caller side. Why do we have to do this?

Something that does work is to use impl AsRef<str> in the argument position, which also makes things simpler for the caller since they only need to provide the return type. But this confuses my understanding of syntactic sugar since I thought impl Trait was syntactic sugar for generics!

So I guess my other question is: When should I use impl Trait vs. a generic in this situation?

fn parse_as_int<N>(key: impl AsRef<str>) -> Result<N, ParseIntError> ...

Either generic arguments are inferred, or you must provide them. If the function has 2 generic arguments, and you supply 1, then which one should the compiler assume you meant? In general, if there are N arguments declared and you specify K (K < N), what should happen? There's simply not enough information there.

The turbofish is not "ugly" (what would be ugly is context-dependent parsing without the leading colons). If you don't want the turbofish, though, then specify the return type as a type annotation on the variable in which you are storing it.

It is (only in argument position — it means something completely different in return position); except for explicitly specifying the generic arguments. Argument-position impl Trait (APIT for short) defines an anonymous type variable that you can't turbofish.

You should never use impl Trait in argument position. It's pointless, redundant, inconsistent, and confusing. Adding it to the language was a mistake. Use impl Trait as a return type only (RPIT).

2 Likes