owned this note
owned this note
Published
Linked with GitHub
---
title: wg-async meeting 2023-08-17
tags: deep-dive, wg-async, minutes
date: 2023-08-17
url: https://hackmd.io/G6ULofyXSIS4CK9u-jwYRg
---
# Agenda
* Initial issue: https://github.com/rust-lang/rust/issues/111546
* Autoderef PR https://github.com/rust-lang/rust/pull/111773
* [Analysis of Rust issue #42940](/83sY9XQjSiK79QdnqK0F3Q)
# Attendance
Present: tmandry, TC, yosh, eholk, vincenzo
# Autoderef on `.await` PR
TC: We discussed this in the meeting on Monday. There seem to be tradeoffs here. At a minimum, it probably needs a crater run.
vincenzo: Looks like that the reason is that we could simplify the API:
from:
```rust!
assert_eq!(shared.as_ref().await.id, 4);
```
to:
```rust!
assert_eq!(shared.await.id, 4);
```
Sure why not? I am not able to see what is the impact of possible api that relay on `as_ref()`?
## Compatibility
tmandry: Can this break existing code? Is this a change we need to consider over an edition?
vincenzo: We should be careful about this, but if it's compatible we should do it.
## Do we feel this just because syntax
tmandry: There's an argument from consistency with method dispatch.
TC: I wonder if we would feel the same way if we had gone with prefix `await` or postfix space syntax.
yosh: I think we would. Striving for async Rust to be like sync Rust, it could make sense.
## "just add a `.await`"
yosh: https://github.com/rust-lang/rust/issues/111546#issuecomment-1557577198
```rust
// sync
fn johns_id() -> Option<u32> {
USER_IDS.get("John Doe").copied()
}
// async, current
fn johns_id_no_autoref() -> Option<u32> {
USER_IDS.force().await.get("John Doe").copied()
}
// async, proposed
fn johns_id_with_autoref() -> Option<u32> {
USER_IDS.await.get("John Doe").copied()
}
```
## Implicit async iterators
Autoref on await would let us do:
```rust
let mut iterator = AsyncCountingIterator::new();
let first = iterator.await;
let second = iterator.await;
```
instead of
```rust
let mut iterator = AsyncCountingIterator::new();
let first = iterator.next().await;
let second = iterator.next().await;
```
## IntoIterator?
tmandry: The other consistency angle I'm thinking of is `into_iterator`. Do we do it there?
```rust
let foo = vec![];
// Calls <Vec<()> as IntoIterator>::into_iter():
for x in foo {}
// Calls <&Vec<()> as IntoIterator>::into_iter():
for x in &foo {}
```
TC: To summarize, when the language uses `into_iter`, as in `for x in y` syntax, it doesn't do `autoderef`. Since `Iterator` and `for` is a similar effect to `Future` and `await`, this is a strong analogy.
tmandry: Agree with that summary.
The original example in the issue was:
```rust
use core::future::IntoFuture;
use futures::future::{ready, Ready};
struct Foo;
impl IntoFuture for &Foo {
type Output = u32;
type IntoFuture = Ready<u32>;
fn into_future(self) -> Self::IntoFuture {
ready(2)
}
}
#[tokio::main]
async fn main() {
assert_eq!(Foo.await, 2);
}
```
```rust
(&USER_IDS).await
```
tmandry: People on the lang team have in the past "threatened" to add `.ref` to make that nicer.
yosh: A different way to reason about it could be we should tweak how `into_iterator` works. (Not actually proposing we should though, but it's an option.)
tmandry: It does make me think here, "why couldn't the user just do an `impl IntoFuture` for Foo"?
yosh: [That seems to work?]( https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f4a947542b4c01973d7a37df960eef27)
eholk: that consumes the `Foo` though. In the example on the issue the thing being awaited is a static so we don't want to (can't?) consume it.
tmandry: Because of postfix `.await`, it's more of a pain to explicitly take a reference. Some lang team members have talked about `.ref` or similar. But we should maybe put more weight on the method dispatch analogy based on this.
TC: What would be the tradeoffs of doing autoderef on `for` loops?
tmandry: It would be confusing.
eholk: With `for` loops there's a convenient place to put the ampersand.
TC: It seems maybe unprincipled to make a decision based only on the syntax that we chose for await rather than its semantic similar to other things in the language.
TC: Do any of the downsides that tmandry mentioned about `for` apply to `.await`?
yosh: Are there any general statements we can make about postfix syntax?
TC: E.g., what if we had postfix `.for(..) { .. }`? How would we feel about that and about the tradeoffs that tmandry raised there?
tmandry: It does feel fine to lean on the syntax for deciding how to look at this.
eholk: `.await` feels like a method call. It's not, but it feels that way.
TC: Interesting aside, during the discussions about choosing the `await` syntax, the fact that postfix dot looked like a field access or method call was one of the downsides people noted.
tmandry: It seems like we need to do a crater run. It's definitely doing to need an FCP from the lang team.
TC: Do we think that t-types will have a feeling on this?
tmandry: Yes, that's a good point, we should collect feedback there. It's be a good stance to say that we want this but that we're cautious about problems.
TC: +1
yosh: Thinking about what you said, TC, about it not being principled.... I do think we want principled designs. If we decide that `.await` should do deref, then we should decide that all postfix syntax should probably do that. We should take that into account. That's definite 100% RFC material and lives on the lang team.
TC: +1
tmandry: +1, there should be some principle.
eholk, vincenzo: +1
TC: Postfix `match` syntax... we couldn't do autoderef for that, that would be weird.
tmandry: Right.
yosh: Is the binding mode thing on `match` related to autoderef?
TC: Not really, it follows a different set of rules.
eholk: Are these under discussion on the language side?
tmandry: We have discussed postfix `match`.
yosh: I know of two examples where postfix `match` is being considered. Here are two examples:
```rust
// for-loops
for match in my_iter {
x => {}
y => {}
}
// try statements
try { .. } match { .. }
```
tmandry: That's different than what I think of with respect to postfix `match`. I think of `.match { .. }`.
## Short reprise of `Future::map`
tmandry: We don't really have time to dive into the second issue.
TC: Maybe we could discuss the items from last week for the people who weren't here.
TC: Here's that meeting: https://hackmd.io/LfWcsTSYTPK5TuORJ4l22g
(Discussion about `Future::map`.)
TC: I did have a thought about this after the meeting last week...
TC: Maybe we should just add this method to a separate trait (that could later be implemented by other things like `Option`). So rather than:
```rust
pub trait Future {
fn map<U, F>(self, f: F) -> Map<Self, F>
where
F: FnOnce(Self::Output) -> U;
}
```
We would add:
```rust
trait MapOnce<T> {
type Wrapped<U>;
fn map<U, F>(self, f: F) -> Self::Wrapped<U>
where
F: FnOnce(T) -> U;
}
```
And we would add an appropriate blanket impl.
(Some discussion followed.)
(The meeting ended here.)