-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
return position impl trait in traits #3193
Conversation
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 In addition, many iterators expose extra inherent methods like Both of these issues can be worked around in the concrete case by every implementer of NewIntoIterator (that wants these benefits) providing an inherent Now of course all of these issues exist from the original God So: does shifting 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 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 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 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. |
} | ||
``` | ||
|
||
## Impl trait must be used in both trait and trait impls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it required that the impl
be exactly the same as the trait
?
impl NewIntoIterator for Vec<u32> {
type Item = u32;
fn into_iter(self) -> impl Iterator<Item = u32> + 'static {
// ~~~~~~~~~ ok or not?
self.into_iter()
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you write the "desugered" version today, you can specify stricter requirements than the trait.
#![feature(type_alias_impl_trait)]
trait MyIntoIterator {
type Item;
type Iterator: Iterator<Item = Self::Item>;
fn my_into_iter(self) -> Self::Iterator;
}
impl<T> MyIntoIterator for Vec<T> {
type Item = T;
type Iterator = impl DoubleEndedIterator<Item = T> + ExactSizeIterator;
fn my_into_iter(self) -> Self::Iterator {
IntoIterator::into_iter(self)
}
}
LGTM, although I have the same impression as @Gankra that due to limited usability this will be used for private stuff, or for public but unstable interface boundaries (e.g. everything in |
Never mind, I see 2071 has mutated into TAITs, which are what are used for the desugaring in this RFC. |
@Gankra all good points, let me attempt to address some of them here.. Why use return
|
I pushed up some edits which I think describe @Gankra's comment, and I wanted to raise what is in my mind the one open question right now: Should we require the impl to match the trait exactly? What if we allowed it to specify a precise type, or additional bounds?The RFC requires the impl to use impl Trait, but is that truly necessary? What if we permitted any sort of type? Then you might have an impl like the following, for example: trait GimmeIterator {
fn gimme_iter(self) -> impl Iterator<Item = u32>;
}
struct MyIterator { .. }
impl GimmeIterator for MyType {
fn gimme_iter(self) -> MyIterator {
MyIterator { ... }
}
} As a result, clients of Why?
Why not?
|
Ooh yes, if you could still name a concrete type that would make the concrete case totally equivalent! You still wouldn't be able to use the refinements generically (still can't write Being able to name So actually in the concrete Although I guess you lose the minor convenience of being able to use the |
I definitely agree that trait impls should be able to overconstrain and provide more than the trait requires. (Similar to the accepted RFC that allows implementing an unsafe fn with a safe fn.)
I think at some later point we're going to need to solve the "name the output type of this function" and that will resolve both this and the ability for a generic function to constrain the return type further. This RFC does not need to and shouldn't discuss such functionality, though, imo. Even without the ability to do so RPIT in traits is extremely valuable. The future feature (if it comes) will then just make it even more functional. |
# Rationale and alternatives | ||
[rationale-and-alternatives]: #rationale-and-alternatives |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why -> impl Trait
before type Foo = impl Trait
?
From a users perspective, it sounds like an impl associated type covers a superset of use cases compared to impl in return position. I am not familiar with the language design or compiler implementation concerns but it sounds like an impl associated type needs to be implemented along the way to impl in return position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type Foo = impl Trait
is already implemented under RFC 2515.
* Introducing a name by converting to camel-case feels surprising and inelegant. | ||
* Return position impl Trait in other kinds of functions doesn't introduce any sort of name for the return type, so it is not analogous. | ||
|
||
There is a need to introduce a mechanism for naming the return type for functions that use `-> impl Trait`; we plan to introduce a second RFC addressing this need uniformly across all kinds of functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note: unsure what all is being discussed in the lead up to this RFC but something I've been playing with in my mind is functions also being types. This would allow Trait::func::ReturnType
.
No idea what has been discussed for a desugared version of -> dyn Trait
but the above idea came up as part my "how would I implement it" brainstorming. a -> dyn Trait
would desugar to Trait::func::alloc_requirements
(size, alignment) and Trait::func::func_into
(wrapper around the user's function, moving into the provided memory)
I'm going to close this RFC -- we've been doing some thinking and I plan to open a revised version that's a bit more ambitious at laying out some of the overall plans. |
I've posted an RFC to address the specific question that came up in this thread about whether impls need to match the trait. |
Thanks for everything here. Was a revised RFC started? I’ve spent some time reading through this RFC issue because I wanted to understand why I couldn’t use Not supporting impl for return types within traits is surprising, particularly given that I can subsequently return a pinned-boxed return value from a function that returns the impl. |
This RFC describes a minimal version of return position impl trait in traits. It is a building block for async function support.
It was developed as part of the impl trait initiative.
Rendered