--- title: "Design meeting 2024-07-18: Open discussion" tags: ["WG-async", "design-meeting", "minutes"] date: 2024-07-18 discussion: https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/Meeting.202024-07-18 url: https://hackmd.io/00pRT3WzTgamk9ocJz-HWQ --- # Discussion ## Attendance - People: TC, eholk, tmandry, Yosh, Daria, David Barsky ## `Move`, `Pin`, `&pin`, etc. (Discussion...) ### Recent posts on this #### Yosh: Safe pin projections through view types (few years old now, but still relevant) https://blog.yoshuawuyts.com/safe-pin-projections-through-view-types/ #### Yosh: Ergonomic self-referential types for Rust https://blog.yoshuawuyts.com/self-referential-types/ #### Yosh: Further simplifying self-referential types for Rust https://blog.yoshuawuyts.com/self-referential-types-2/ #### boats: How could Pin be easier? https://gist.github.com/withoutboats/3f91f115bc4e5352952feb637383c73a #### boats: A set of features to make `Pin` easier to use https://gist.github.com/withoutboats/b5b16988e32f49a02733b668cf892711 #### eholk: Two Ways Not To Move https://theincredibleholk.org/blog/2024/07/15/two-ways-not-to-move/ ### Missing supertype TC: Something I've been thinking about and discussing with others is that we seem to be missing a supertype of `&mut T` and `Pin<&mut T>`. That supertype would allow both `&mut T` and `Pin<&mut T>` to safely coerce to it, but it would not provide the ability to move out of it or the guarantee that the thing is pinned. That's what most functions should actually use in signatures. Migration, of course, would be somewhere between thorny and completely infeasible. TC: I had struggled for a name for this. I called it `&write`. In talking with Niko, he had earlier had a similar thought, and called it `&uniq`, which seems probably better. TC: People sometimes talk about wanting to not add more `Pin`-based APIs, but in this framing, it occurs to me that it's actually adding more `&mut` APIs that might be the problem (not that there's anything we can do about that). ### Is movability orthogonal? | | Move | !Move | |-|------|-------| | Read | `&T` | `&pinned T` | | Write | `&mut T` | `Pin<&mut T>` or `&pinned mut T` | | Destroy | `&owned T` | `Pin<&owned T>` or `&pinned owned T` | Daria: I kinda had this in mind: Read < Read-Write < Read-Write-Destroy Daria: I had an idea about having a `&ReadOnlyCell<T>` that implements `From<&T>` and `From<&Cell<T>>` for calling specific foreign functions from some libraries. Basically it's about combining `slice::copy_within` and `slice::copy_from_slice`. I think it's all about types being (conjuctions of) predicates. eholk: Use case for `&pinned T`: https://crates.io/crates/pin-cell Tmandry: if a type is pinned, you know you can take a stable pointer to it. That's one use case for `&pinned` types. This is useful for things like self-referential pointers and intrusive collections. Daria: maybe post-monomorphization analysis could help with migration to "new more restrictive references". Well, you can transmute new restrictive reference into old reference and make unsafe assertion by user that everything is fine. ### Global analysis? The old version of `Move` probably requires global analysis, which means the programmer will have to do it because Rust doesn't really do global analysis. `&pinned mut T` can be done completely locally to a function, since they can only be created from a `T` or derived from a `&pinned mut T`. ### Pinned references have provenance vibes From RFC 3559: ```rust fn main() { unsafe { let mut x = 5; // Setup a mutable raw pointer and a shared reference to `x`, // and derive a raw pointer from that shared reference. let ptr = &mut x as *mut i32; let shrref = &*ptr; let shrptr = shrref as *const i32 as *mut i32; // `ptr` and `shrptr` point to the same address. assert_eq!(ptr, shrptr); // And yet, while writing to `ptr` here is perfectly fine, // the next line is UB! shrptr.write(0); // alternative: `ptr.write(0);` } } ``` ## async closures Daria: If we pick `AsyncFn*` we can do `async Fn*` later :) TC: The open syntax question is whether we want: ```rust fn f() -> impl async Fn() -> u8 { async fn g() -> u8 { 42 } async || g().await } ```