What does the feature "impl Trait" mean?


#1

I found that there is a new feature in 1.26, with which one can write:

pub fn (...) -> impl SomeTrait{...}

Then what does this mean? What is its relation to static dispatch, which is based on generic and dynamic dispatch, which is based on trait object? And is the impl Trait feature static or dynamic dispatch?

Sorry to ask this maybe stupid question, but the latest rust book seemingly has not yet covered this feature.


#2

From what I can tell, the book doesn’t cover it, doesn’t mention it in the appendices, and it’s not in the reference, even under the “not-yet-documented features” section.

So far as I can tell, the only documentation is linked from the tracking issue that links to the original RFC 1522, plus revision issues.

The reasoning appears to be that the feature isn’t quite finished yet, but publicizing an (apparently) totally undocumented feature doesn’t really show the language in the best light.

Short version: impl Trait is just a way to avoid having to name a type, and instead just naming a trait with which you operate on it. It uses static dispatch.

Edit: I just double-checked and -> impl doesn’t show up anywhere in the git source for the book.


#3

Then what does this mean?

It means that function returns some specific type that implements SomeTrait.

What is its relation to static dispatch, which is based on generic and dynamic dispatch, which is based on trait object?

The exact type is always known to the compiler, so no dynamics is involved.

What impl Trait buys you is that even though the type is well defined and know, you don’t have to name it, which helps with numerous edge cases in the language and is clearer for users of the function.
Knowing that the result implements SomeTrait is often all you care about, while knowing exact type name
would often not be very useful. This also allows you to expose SomeTrait as public interface, while keeping type name as internal detail.


#4

In addition, it allows you to return unnameable types without needing to erase their type (ie trait object). The classic example is:

fn foo<I: Iterator<Item = i32>>(iter: I) -> impl Iterator<Item = i32> {
      // closures are unnameable 
      iter.map(|x| x * 2)
}

This makes constructs like the above (and similarly with Future) more efficient.


#5

blog is currently best starting point.

Note: impl Trait is adding two features, return & argument position.

Argument impl Trait. Is supposedly to make code easier for newcomers. (Time will tell.)

Return impl Trait. Has the benefit of encapsulating the internal type, detail that you don’t need to know and without can make error messages long when that type is composed of many nested generics. (see blog.)


#6

IMHO, this feature was absolutely unnecessary - it adds zero new capabilities and only takes some away (ie cannot use turbofish to select the type). Now we have 3 ways to specify generic type parameters with bounds. I sincerely hope Rust doesn’t start a trend of bending over backwards for some perceived benefit for newcomers.

impl trait in the return position, however, was dearly needed.


#7

The inability to do turbofish can be a valuable feature for API design when used intelligently. For example in serde_json::from_reader we know that users practically never want to turbofish the R type parameters and quite commonly want to turbofish the T type parameter. Currently they are forced to write:

serde_json::from_reader::<_, MyType>(...)

We can let them write the following cleaner code by changing the R type parameter to impl Read.

serde_json::from_reader::<MyType>(...)

#8

That’s kind of neat but I don’t see enough value from this (I surmise) niche case.

I still believe impl trait in arg position is a superfluous change and just adds yet another way to do something that can already be done well with existing features. We’re going to see codebases using all 3 styles, and it’s a needless distraction. The where clause at least added something you couldn’t do with T: SomeTrait - namely, being able to say, e.g., SomeConcreteType: From<T>. Rust has much more pressing needs than this, and I (selfishly!) wish people would focus on them instead of stuff like this. Just my $.02 of course.


#9

I guess I don’t see how this follows after I showed a real life example of a convenient API that cannot be expressed well using previously existing features.


#10

I don’t think <_, Type> qualifies as not being expressed well, particularly when underscore is already used in a bunch of places to infer types, elide a lifetime param, and for ignoring results. I also think this is a somewhat niche case, as I mentioned. How many people are going to be aware of this artifact when they’re designing their generic APIs? And in how many places will this really be necessary? This also kicks in where type inference can’t deduce the type and the caller/user isn’t using let bindings with a type.

As mentioned, it’s a neat “trick” but completely unnecessary in the grand scheme of things. Again, just my opinion. I’d love for there to be more focus on more pressing needs that actually advance Rust.


#11

In the run-up to the edition, the docs are getting a little weird. The 2018 edition of the book will have docs, but doesn’t yet. https://github.com/rust-lang/book/issues/1291

I’ve put the book on a bit of a backburner in order to ship https://github.com/rust-lang-nursery/edition-guide ASAP. It does have some initial documentation https://rust-lang-nursery.github.io/edition-guide/2018/transitioning/traits/impl-trait.html

This is purely a “I’m only one person and there’s a ton of stuff to do and priorities are hard” situation.


#12

You’re telling us you need more minions? Didn’t you already lure Carol into your doc dungeon?

Maybe if we got an infinite number of crabs and a whole bunch of typewriters… not sure where we’d get enough ink ribbons, though…


#13

:face_with_raised_eyebrow::