Skip to content

Commit d01b3f2

Browse files
committed
Clean up whitespace
1 parent 6e9855a commit d01b3f2

File tree

1 file changed

+30
-31
lines changed

1 file changed

+30
-31
lines changed

text/3654-return-type-notation.md

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
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>` or `T::method(..): Send`. The primary use case is to add bounds such as `Send` to the futures returned by `async 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).
1212

13-
This RFC proposes a new kind of type written `<T as Trait>::method(..)` (or `T::method(..)` for short). RTN refers to "the type returned by invoking `method` on `T`".
13+
This RFC proposes a new kind of type written `<T as Trait>::method(..)` (or `T::method(..)` for short). RTN refers to "the type returned by invoking `method` on `T`".
1414

1515
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](#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](https://rust-lang.github.io/api-guidelines/future-proofing.html).
1616

@@ -43,7 +43,7 @@ To create an interoperable async ecosystem, we need the ability to write a singl
4343
```rust
4444
trait Service<Request> {
4545
type Response;
46-
46+
4747
// Invoke the service.
4848
async fn call(&self, req: Request) -> Self::Response;
4949
}
@@ -60,7 +60,7 @@ where
6060
R: Debug,
6161
{
6262
type Response = S::Response;
63-
63+
6464
async fn call(&self, request: R) -> S::Response {
6565
eprintln!("{request:?}");
6666
self.0.call(request).await
@@ -73,7 +73,7 @@ Defining `Service` as shown above works fine in a thread-per-core or single-thre
7373

7474
```rust
7575
async fn spawn_call<S>(service: S) -> S::Response
76-
where
76+
where
7777
S: Service<(), Response: Send> + Send + 'static,
7878
{
7979
tokio::spawn(async move {
@@ -108,7 +108,7 @@ The only way today to make this code compile is to modify the `Service` trait de
108108
```rust
109109
trait SendService<Request>: Send {
110110
type Response;
111-
111+
112112
// Invoke the service.
113113
fn call(
114114
&self,
@@ -127,7 +127,7 @@ It is useful to compare this situation with analogous scenarios that arise elsew
127127
fn into_iter_example<I: IntoIterator>(i: I) {
128128
let iter = i.into_iter();
129129
std::thread::spawn(move || {
130-
iter.next(); // <-- Error!
130+
iter.next(); // <-- Error!
131131
});
132132
}
133133
```
@@ -147,15 +147,15 @@ error[E0277]: `<I as IntoIterator>::IntoIter` cannot be sent between threads saf
147147
| _____|__________________within this `{closure@src/lib.rs:3:24: 3:31}`
148148
| | |
149149
| | required by a bound introduced by this call
150-
4 | | iter.next();
150+
4 | | iter.next();
151151
5 | | });
152152
| |_____^ `<I as IntoIterator>::IntoIter` cannot be sent between threads safely
153153
...
154154
help: consider further restricting the associated type
155155
|
156156
1 | fn into_iter_example<I: IntoIterator>(i: I)
157157
| where <I as IntoIterator>::IntoIter: Send {
158-
|
158+
|
159159
```
160160

161161
There are two ways the function `into_iter_example` could be made to compile:
@@ -173,11 +173,11 @@ The core feature proposed in this RFC is the ability to write a bound that bound
173173

174174
```rust
175175
async fn spawn_call<S>(service: S) -> S::Response
176-
where
176+
where
177177
S: Service<
178178
(),
179179
Response: Send,
180-
call(..): Send, // 👈 "the method `call`
180+
call(..): Send, // 👈 "the method `call`
181181
// returns a `Send` future"
182182
> + Send + 'static,
183183
{
@@ -239,7 +239,7 @@ We expect most users in the wild to define "trait aliases" to indicate cases whe
239239
// by async or `-> impl Trait` methods
240240
trait Service<Request> {
241241
type Response;
242-
242+
243243
// Invoke the service.
244244
async fn call(&self, req: Request) -> Self::Response;
245245
}
@@ -250,7 +250,7 @@ The expansion of this macro use RTN to create a trait that both (1) implies a `S
250250
```rust
251251
trait SendService<R>: // a `SendService` is...
252252
Service< // ...a `Service`...
253-
R,
253+
R,
254254
call(..): Send, // ...where `call` returns
255255
// a `Send` future...
256256
> +
@@ -267,7 +267,7 @@ The function `spawn_call` can then be written as follows:
267267

268268
```rust
269269
async fn spawn_call<S>(service: S) -> S::Response
270-
where
270+
where
271271
S: SendService<(), Response: Send> + 'static,
272272
// 👆 use the alias
273273
{
@@ -320,7 +320,7 @@ For traits whose futures may or may not be `Send`, the recommend pattern is to l
320320
#[trait_variant::make(SendHealthCheck: Send)]
321321
trait HealthCheck {
322322
async fn check(&mut self, server: &Server) -> bool;
323-
323+
324324
async fn shutdown(&mut self, server: &Server);
325325
}
326326
```
@@ -337,7 +337,7 @@ impl HealthCheck for DummyCheck {
337337
async fn check(&mut self, server: &Server) -> bool {
338338
true
339339
}
340-
340+
341341
async fn shutdown(&mut self, server: &Server) {}
342342
}
343343
```
@@ -375,9 +375,9 @@ where
375375
// be Send because this code runs.
376376
tokio::time::sleep(Duration::from_secs(1)).await;
377377
}
378-
378+
379379
emit_failure_log(&server).await;
380-
380+
381381
server.shutdown().await;
382382
// ----- Error: Returned future must be Send
383383
// because this code runs.
@@ -416,7 +416,7 @@ For example, the following function spawns a task to shutdown the server:
416416
fn spawn_shutdown<HC>(health_check: H, server: Server)
417417
where
418418
HC: SendHealthCheck + 'static,
419-
// --------------- stricter than necessary
419+
// --------------- stricter than necessary
420420
{
421421
tokio::spawn(async move {
422422
server.shutdown().await;
@@ -444,7 +444,7 @@ where
444444
The `shutdown(..)` notation acts like an associated type referring to the return type of the method.
445445
The bound `HC: HealthCheck<shutdown(..): Send>` indicates that the `shutdown` method,
446446
regardless of what arguments it is given,
447-
will return a `Send` future.
447+
will return a `Send` future.
448448
These bounds do not have to be written in the `HealthCheck` trait, it could also be written as follows:
449449

450450
```rust
@@ -574,7 +574,7 @@ Examples: given the `Widgets` trait defined earlier in this section...
574574
* `T: Widgets<widgets(..): Send>` is a valid associated type bound
575575

576576
RTN bounds are internally desugared to an RTN in a standalone where-clause,
577-
so e.g. `where T: Widgets<widgets(..): Send>` becomes `where <T as Widgets>::widgets(..): Send`.
577+
so e.g. `where T: Widgets<widgets(..): Send>` becomes `where <T as Widgets>::widgets(..): Send`.
578578
We will not consider them further in this section.
579579

580580
## Where RTN can be used (for now)
@@ -690,7 +690,7 @@ The Async Working Group has performed [five case studies][cs] around the use of
690690
We found that all of these key use cases required a way to handle send bounds, with only two exceptions:
691691

692692
* `embassy`, where the entire process is single-threaded (and hence `Send` is not important),
693-
* Fuchsia, where the developers at first thought they needed `Send` bounds, but ultimately found they were able to refactor so that spawns did not occur in generic code ([link to the relevant section](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/socket-handler.html#send-bound-limitation)).
693+
* Fuchsia, where the developers at first thought they needed `Send` bounds, but ultimately found they were able to refactor so that spawns did not occur in generic code ([link to the relevant section](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/socket-handler.html#send-bound-limitation)).
694694

695695
From this we conclude that offering async functions in traits without *some* solution to the "send bound problem" means it will not be usable for most Rust developers. The Fuchsia case also provides evidence that, even when workarounds exist, they are not obvious to Rust developers.
696696

@@ -714,7 +714,7 @@ Versus aliases that always bound every method, RTN can be used to
714714
* bound individual methods
715715
* introduce bounds for traits other than `Send`.
716716

717-
As [described in the motivation](#bounding-specific-methods), bounding individual methods allows for greater reuse.
717+
As [described in the motivation](#bounding-specific-methods), bounding individual methods allows for greater reuse.
718718
For functions that only make use of a subset of the methods in a trait, RTN can be used to create a "maximally reusable" signature.
719719

720720
## What other syntax options were considered?
@@ -737,7 +737,7 @@ The document reviewed the following designs overall:
737737
We briefly review the key arguments here:
738738

739739
* "StatusQuo": `D: Database<items(): DoubleEndedIterator>`
740-
* This notation is more concise and feels less heavy-weight. However, we expect users to primarily use aliases; also, the syntax "feels" surprising to many users, since Rust tends to use `..` to indicate elided items. The biggest concern here is a potential future conflict. If we (a) extend the notation to allow argument types to be specified ([as described in the future possibilities section](#future-possibilities)) AND (b) support some kind of variadic arguments, then `D::items()` would most naturally indicate "no arguments".
740+
* This notation is more concise and feels less heavy-weight. However, we expect users to primarily use aliases; also, the syntax "feels" surprising to many users, since Rust tends to use `..` to indicate elided items. The biggest concern here is a potential future conflict. If we (a) extend the notation to allow argument types to be specified ([as described in the future possibilities section](#future-possibilities)) AND (b) support some kind of variadic arguments, then `D::items()` would most naturally indicate "no arguments".
741741
* "Return": `D: Database<items::return: DoubleEndedIterator>`
742742
* This notation avoids looking like a function call. Many team members found it dense and difficult to read. While intended to look more like an associated type, the use of a lower-case keyword still makes it feel like a new thing. The syntax does not support future extensions (e.g., specifying the value of argument types).
743743
* "Output": `D: Database<items::Output: DoubleEndedIterator>` (see [this blog post](https://smallcultfollowing.com/babysteps/blog/2023/06/12/higher-ranked-projections-send-bound-problem-part-4/) for details)
@@ -747,7 +747,7 @@ We briefly review the key arguments here:
747747

748748
## Why not use `typeof`, isn't that more general?
749749

750-
The compiler currently supports a `typeof` operation as an experimental feature (never RFC'd). The idea is that `typeof <expr>` type-checks `expr` and evaluates to the result of that expression. Therefore `typeof 22_i32` would be equivalent to `i32`, and `typeof x` would be equivalent to whatever the type of `x` is in that context (or an error if there is no identifier `x` in scope).
750+
The compiler currently supports a `typeof` operation as an experimental feature (never RFC'd). The idea is that `typeof <expr>` type-checks `expr` and evaluates to the result of that expression. Therefore `typeof 22_i32` would be equivalent to `i32`, and `typeof x` would be equivalent to whatever the type of `x` is in that context (or an error if there is no identifier `x` in scope).
751751

752752
It might appear that `typeof` can be used in a similar way to RTN, but in fact it is significantly more complex. Consider our first example, the `HealthCheck` trait:
753753

@@ -788,7 +788,7 @@ where
788788
H: HealthCheck + Send + 'static,
789789
typeof {
790790
let hc: &'a mut H;
791-
let s: Server;
791+
let s: Server;
792792
H::check(hc, s)
793793
}: Send,
794794
```
@@ -935,7 +935,7 @@ Second, it covers far fewer use cases than RTN: it cannot be used to express spe
935935

936936
## Why not Send trait transformers?
937937

938-
[Trait transformers][] are a proposal to have "modifiers" on trait bounds that produce a derived version of the trait. For example, `T: async Iterator` might mean "T implements a version of `Iterator` where the `next` function is `async`". Following this idea, one can imagine `T: Send HealthCheck` to mean "implement a version of `HealthCheck` where every async fn returns a `Send` future". This idea is an ergonomic way to manage traits that have a lot of async functions, as [came up in the Microsoft case study](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/microsoft.html#send-bounds).
938+
[Trait transformers][] are a proposal to have "modifiers" on trait bounds that produce a derived version of the trait. For example, `T: async Iterator` might mean "T implements a version of `Iterator` where the `next` function is `async`". Following this idea, one can imagine `T: Send HealthCheck` to mean "implement a version of `HealthCheck` where every async fn returns a `Send` future". This idea is an ergonomic way to manage traits that have a lot of async functions, as [came up in the Microsoft case study](https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/microsoft.html#send-bounds).
939939

940940
[Trait transformers]: https://smallcultfollowing.com/babysteps/blog/2023/03/03/trait-transformers-send-bounds-part-3/
941941

@@ -967,11 +967,11 @@ Prior to stabilizing the "associated type position" syntax, we should be sure we
967967

968968
>Think about what the natural extension and evolution of your proposal would
969969
be and how it would affect the language and project as a whole in a holistic
970-
way.
970+
way.
971971

972972
## Implementing trait aliases
973973

974-
Referring to the `Service` trait specifically,
974+
Referring to the `Service` trait specifically,
975975
the Tokio developers expressed a preference to name the "base trait" `LocalService`
976976
and to call the "sendable alias" `Service`.
977977
This reflects the way that Tokio uses work-stealing executors by default.
@@ -981,7 +981,7 @@ This formulation can be done with `trait_variant` like so
981981
#[trait_variant::make(Service: Send)]
982982
trait LocalService<R> {
983983
type Response;
984-
984+
985985
async fn call(&self, request: R) -> Self::Response;
986986
}
987987
```
@@ -1084,7 +1084,7 @@ fn load_data<D: DataFactory>(data_factory: D) {
10841084
// value.
10851085
await_future(load_future);
10861086
}
1087-
1087+
10881088
fn await_future<D: DataFactory>(load_future: D::load(..)) -> Data {
10891089
// -------
10901090
// As above, expands to `D::load(&'_ D)`, which
@@ -1110,4 +1110,3 @@ We expect to make traits with async functions and RPITIT dyn safe in the future
11101110
## Naming the zero-sized types for a method
11111111

11121112
Every function and method `f` in Rust has a corresponding zero-sized type that uniquely identifies `f`. The RTN notation `T::check(..)` refers to the return value of `check`; conceivably `T::check` (without the parens) could be used to refer the type of `check` itself. In this case, `T::check(..)` can be thought of as shorthand for `<T::check as Fn<_>>::Output`.
1113-

0 commit comments

Comments
 (0)