12

return position impl trait in traits by nikomatsakis · Pull Request #3193 · rust...

 2 years ago
source link: https://github.com/rust-lang/rfcs/pull/3193
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Copy link

Contributor

Gankra commented 11 days ago

edited

From an API-design perspective, the example of IntoIterator is quite significant! (To be clear, I know you're not proposing replacing IntoIterator, but I am considering how NewIntoIterator limits the usefulness of IntoIterator, which generalizes to future consumers of this feature.)

Although it is only required that type IntoIter: Iterator we have a zoo of very important traits which refine Iterator (DoubleEndedIterator, ExactSizeIterator). So using impl Iterator instead would prevent e.g. vec.into_iter().rev() from working.

In addition, many iterators expose extra inherent methods like std::vec::IntoIter::as_slice() which are niche but extremely useful when they come up.

Both of these issues can be worked around in the concrete case by every implementer of NewIntoIterator (that wants these benefits) providing an inherent into_iter method that shadows the trait version in concrete contexts. This is not an uncommon pattern (especially for types where the trait method is "core functionality" that they want to be in scope even when the trait is not), but it is annoying. By default folks will not do this extra work, limiting the usefulness of all the machinery built around DoubleEndedIterator and ExactSizeIterator.

Now of course all of these issues exist from the original impl Trait feature, and I am an avid user of -> impl Iterator! But -> impl Iterator I regard as more for... "whatever" iterators. It's a slam dunk for internal APIs where I know the Iterator Zoo doesn' t matter. I do also use it for public APIs though, for iterators where I wouldn't put in the effort to forward all the Iterator Zoo methods on my newtype wrapper anyway. So at that point the only real regression is being able to name the type.

God -> impl Trait is a great feature. heart_eyes_cat

So: does shifting impl Trait from something that can be used at your discretion to something mandated by the Trait itself significantly change the calculus? A little bit! It breaks the ability to write generic Iterator code that requires Iterator Zoo refinements. For instance, I believe it would become impossible to write:

fn is_palindrome<Iter, T>(iterable: Iter) -> bool
  where Iter: NewIntoIterator<Item=T>,
   Iter::IntoIter: DoubleEndedIterator,
   T: Eq;

Instead, you would have to avoid accepting anything that was IntoIter and instead directly accept a DoubleEndedIterator. From a usability perspective this means requiring users to run is_palindrome(slice.into_iter()) vs the usual is_palindrome(slice).

A minor papercut perhaps, but it defeats the entire purpose of having an IntoIterator trait if it can't be used generically! You would need to introduce IntoDoubleEndedIterator, IntoExactSizeIterator, etc, and force everyone to implement all the applicable ones.

Now of course, the Iterator traits are basically the most extreme usage of traits you can find. They're part of the language (insofar as for implicitly invokes it). There's tons of implementations (and you wouldn't bat an eye at a random crate implementing it!). It's performance sensitive. It has a Zoo of refining traits. It has a million subtle little power-user features.

There are plenty of traits where these restrictions are basically fine. Is that the case for the async traits this feature is intended for? I would love to have more motivating details included so we know we're actually solving the problem we intend to.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK