-
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
RFC: Return Type Notation #3654
Conversation
text/0000-return-type-notation.md
Outdated
There are multiple ways we could write this where-clause, varying in their specificity... | ||
|
||
* `where C::capture(..): Send` -- this indicates that `C::capture()` will return a `Send` value for any possible set of parameters | ||
* `where C::capture(&mut C, i32): Send` -- this indicates that `C::capture()` will return a `Send` value when invoked specifically on a `&mut C` (for the `self` parameter) and an `i32` |
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.
wouldn't this bring back the ambiguity with Fn()
? for example when we have fn capture() -> impl Future
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.
Indeed, it would. fn f() where C::capture(&mut C, i32): Send {}
is already syntactically legal today. The parentheses denote parenthesized type arguments which may be attached to arbitary type paths (and it also comes with turbofish for expression and pattern paths: C::capture::(&mut C, i32)
).
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.
It is syntactically legal, yes, but not meaningful semantically.
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.
Semantically as well if C
is a module but yeah, I don't think it can actually meaningfully clash if C
is a type parameter since types and modules live in the same namespace.
//@ edition: 2015
//@ check-pass
mod C { pub use std::ops::Fn as capture; }
fn f() where C::capture(): 'static {}
text/0000-return-type-notation.md
Outdated
|
||
We encountered a number of problems with this design. | ||
|
||
### If the name is implicit, what name should we use? |
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.
you could also use one of those #
namespace such as fn#widgets
or return#widgets
or result_of#widgets
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.
yes, I'll add that as a possibility
I would like to propose a tweak to the desguaring of RTN, which simplifies the limitation to "only in the The RFC describes the By comparison, while normal associated types do support a projection syntax, they also crucially support a syntax for naming and unification. For example, given the bound This is sort of the type level analog to So the tweak I propose is this: This opens the door for |
Thanks kennytm! Co-authored-by: kennytm <kennytm@gmail.com>
I like the suggestion of It seems to me to be more of a change in how we think about things, is that correct? |
I believe that is correct, because of the limitation to It does also change how we might relax that limitation in the future, though- if trait DataFactory {
async fn load(&self) -> Data;
}
fn load_data<D: DataFactory<load(..) -> L>, L>(data_factory: D) {
let load_future: L = data_factory.load();
await_future(load_future);
}
fn await_future<D: DataFactory<load(..) -> L>, L>(argument: L) -> Data {
argument.await
}
struct Wrap<'a, D: DataFactory<load(&'a D) -> L>, L> {
load_future: L, // the future returned by `D::load`.
} This does look like it would subtly change some quantifier scopes, but maybe they would wind up being closer to what you would get if you wrote this out by hand with generic associated types? |
The RFC is written with RTNs acting as a kind of "pseudo-associated type"1. The idea is that you can think of traits as having an internal I agree with your suggestion that should also mean being able to do But I don't quite follow why we would want to rewrite the I guess my point of confusion is this paragraph that you wrote:
Again here I think that making the Footnotes
|
Ah, I could have spelled that out more clearly- the issue I see is that if It's probably a good idea to have a projection syntax as well, but since the RFC limits the |
The RFC doesn't limit it to bounds, it limits it to "self position" -- i.e., the RFC allows for I see your point about consistency, but there are other kinds of consistency to consider, e.g., I think I will add this as an "unresolved question". I expect we will stabilize the bound form first anyway for implementation reasons. |
Right, this is all I meant by "limited to bounds." I was thinking of I guess if we consider that form to be rare enough (i.e. we expect everyone to use |
The fn start_health_check<H>(health_check: H, server: Server)
where
H: HealthCheck + Send + 'static,
for<'a> typeof for (health_check: &'a mut H, server: Server) H::check(health_check, server): Send, or even fn start_health_check<H>(health_check: H, server: Server)
where
H: HealthCheck + Send + 'static,
for<'a> typeof for (health_check: &'a mut H, server: Server) health_check.check(send): Send, This avoids the need for a |
Co-authored-by: zachs18 <8355914+zachs18@users.noreply.github.com>
github markdown makes newlines significant
@bluebear94 I pushed a variant of your proposal using |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as off-topic.
This comment was marked as off-topic.
@rfcbot fcp merge This proposal has been under discussion for a long time and, in this first week, all the feedback has been incorporated. I'm going to go ahead and kick off the checkboxes towards final-comment-period. |
Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
I'd love to summaries here discussion around these couple of posts:
While I don't think this summary should affect the specific decision on this RFC, I think it helps The main motivation for RTN is to be able to say that a particular future is Send. We care about Peeling Rust specific abstractions, the picture is this: We have an asynchronous computation. The computation is suspended, meaning that it's not actually
It is important to realize that this claim is actually wrong. It is totally fine to migrate async
Non-the-less, it would be wrong to just remove async fn sneaky() {
thread_local! { static TL: Rc<()> = Rc::new(()); }
let rc = TL.with(|it| it.clone());
async {}.await;
rc.clone();
} One can imagine alternative world, where:
In that world, both work-stealing and thread-per-core executors would work fine with non- In other words, this is an instance of a classic Rust problem of composing unsafe abstractions. The two unsafe abstractions here are:
Each can be made safe in isolation, but the combination of the two end up being unsound. From where I stand, I am 0.85 sure that we've picked the wrong one of the two in this case: I'll That being said, I don't see a reasonable way we can rectify the mistake while maintaining backwards So it seems reasonable to accept this part of Rust as is, and instead try to address the |
@rfcbot reviewed |
@rfcbot fcp reviewed |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
…estebank Change return-type-notation to use `(..)` Aligns the syntax with the current wording of [RFC 3654](rust-lang/rfcs#3654). Also implements rustfmt support (along with making a match exhaustive). Tracking: * rust-lang#109417
Rollup merge of rust-lang#127092 - compiler-errors:rtn-dots-redux, r=estebank Change return-type-notation to use `(..)` Aligns the syntax with the current wording of [RFC 3654](rust-lang/rfcs#3654). Also implements rustfmt support (along with making a match exhaustive). Tracking: * rust-lang#109417
[RTN] Fix typo: "h" -> "hc" & "ds" -> "s"
4ddd80d
to
3504c71
Compare
We've been using "tracking issue" rather than "rust issue" recently, so let's adjust that. Let's also use sentence case for the title and remove some remaining bits left over from the RFC template. And let's replace some Unicode characters that don't have ubiquitous font support with ASCII equivalents.
The team has accepted this RFC, and it's now been merged. Thanks to @nikomatsakis for pushing forward on this important work, to @compiler-errors for the experimental implementation, and to all those who reviewed the RFC and provided useful feedback. For further updates on this work, follow the tracking issue: |
…kh726 Implement Return Type Notation (RTN)'s path form in where clauses Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now): > As a standalone type, RTN can only be used as the Self type of a where-clause [...] Specifically, in order to enable code like: ```rust trait Foo { fn bar() -> impl Sized; } fn is_send(_: impl Send) {} fn test<T>() where T: Foo, T::bar(..): Send, { is_send(T::bar()); } ``` * In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method. * When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position. * In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds). This PR also adds a bunch of tests, mostly negative ones (testing error messages). In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted. cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang#109417
…kh726 Implement Return Type Notation (RTN)'s path form in where clauses Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now): > As a standalone type, RTN can only be used as the Self type of a where-clause [...] Specifically, in order to enable code like: ```rust trait Foo { fn bar() -> impl Sized; } fn is_send(_: impl Send) {} fn test<T>() where T: Foo, T::bar(..): Send, { is_send(T::bar()); } ``` * In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method. * When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position. * In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds). This PR also adds a bunch of tests, mostly negative ones (testing error messages). In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted. cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang#109417
Rollup merge of rust-lang#129629 - compiler-errors:rtn-in-path, r=jackh726 Implement Return Type Notation (RTN)'s path form in where clauses Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now): > As a standalone type, RTN can only be used as the Self type of a where-clause [...] Specifically, in order to enable code like: ```rust trait Foo { fn bar() -> impl Sized; } fn is_send(_: impl Send) {} fn test<T>() where T: Foo, T::bar(..): Send, { is_send(T::bar()); } ``` * In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method. * When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position. * In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds). This PR also adds a bunch of tests, mostly negative ones (testing error messages). In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted. cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang#109417
Implement Return Type Notation (RTN)'s path form in where clauses Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now): > As a standalone type, RTN can only be used as the Self type of a where-clause [...] Specifically, in order to enable code like: ```rust trait Foo { fn bar() -> impl Sized; } fn is_send(_: impl Send) {} fn test<T>() where T: Foo, T::bar(..): Send, { is_send(T::bar()); } ``` * In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method. * When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position. * In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds). This PR also adds a bunch of tests, mostly negative ones (testing error messages). In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted. cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang/rust#109417
Return type notation (RTN) gives a way to reference or bound the type returned by a trait method. The new bounds look like
T: Trait<method(..): Send>
orT::method(..): Send
. The primary use case is to add bounds such asSend
to the futures returned byasync fn
s in traits and-> impl Future
functions, but they work for any trait function defined with return-position impl trait (e.g.,where T: Factory<widgets(..): DoubleEndedIterator>
would also be valid).This RFC proposes a new kind of type written
<T as Trait>::method(..)
(orT::method(..)
for short). RTN refers to "the type returned by invokingmethod
onT
".To keep this RFC focused, it only covers usage of RTN as the
Self
type of a bound or where-clause. The expectation is that, after accepting this RFC, we will gradually expand RTN usage to other places as covered under Future Possibilities. As a notable example, supporting RTN in struct field types would allow constructing types that store the results of a call to a trait-> impl Trait
method, making them more suitable for use in public APIs.Examples of RTN usage allowed by this RFC include:
where <T as Trait>::method(..): Send
where T: Trait<method(..): Send>
where T::method(..): Send
Trait
is inferred from the compiler)dyn Trait<method(..): Send>
dyn
types take lists of bounds)impl Trait<method(..): Send>
impl
types)Rendered
Tracking: