7 ms·
> the point of Pin is to wrap types that CAN move. I would highlight that there are many cases where you CAN move an object safely until a certain operation re
by Dagonfly 1mo ago
> the point of Pin is to wrap types that CAN move.
I would highlight that there are many cases where you CAN move an object safely until a certain operation requires the object to "stay put" in place.
Pin allows for that by tying the object to the place only when required. That's why Pin relates to both the object and the place.
Meanwhile, !Move types can't ever move. The object has to remain in the inital place it was constructed in. !Move requires in-place construction and emplacement to be ergonomic at all.
- Mond_ 1mo agoStupid question, can't this trivially be solved by having a movable constructor / builder type that then gets turned into a non-movable type when built?
- Dagonfly 1mo agoSure, thats one reason why IntoFuture and Future exist. Imo, in hindsight this is also main mistake in aysnc Rust: The whole async system should be build around IntoFuture rather than Future (async fn should return impl IntoFuture). That way you could pass around IntoFutures without being affected by auto traits leaking. Only when you actually call .await() or .poll() would the immovable Future materialize.
- yoshuaw 1mo agoYou're right that this is an important issue. I wrote a post explaining this in some detail, so at the very least we avoid having the same problem with generator functions: https://blog.yoshuawuyts.com/gen-auto-trait-problem https://blog.yoshuawuyts.com/gen-auto-trait-problem
- stymaar 1mo agoThanks for the explanation (though I have to admit I liked your old blog theme more). Since it's just a matter of how async get desugared, can it be changed through an edition?
- Dagonfly 1mo agoOh yeah, I've read about all your blog post on this topic :) To dump some ideas on you: I think one missing piece might be that FnOnce() -> impl Future should implement IntoFuture. Async runtimes would then use IntoFuture in their APIs agressively. I call this a "workload blueprint" at work. Its a closure/type that contains all the info to start the workload, but in a minimal form. In Rust terms this would be a buildprint that is ideally Send + Move + Forget + 'static, even if the actual work (and the backing struct of the Future) is !Send (e.g. It holds an Rc across await points). Runtimes could use this for their advantage: There would be a global pool of "workload blueprint" that can be stolen by any executer thread, but once a !Send workload has started on one thread it can't be migrated to another. This in combination with matklad's ideas about seperating TaskSend from ThreadSend (https://matklad.github.io/2023/12/10/nsfw.html https://matklad.github.io/2023/12/10/nsfw.html) would solve most of my async pain points.
- 10000truths 1mo ago> I would highlight that there are many cases where you CAN move an object safely until a certain operation requires the object to "stay put" in place. You could model this with a state machine enum where the "stay put" phase is a variant that accepts a !Move, like so: enum StateMachine<PinnedState: ?Move> { InitialState, State1(String), State2(Box<PinnedState>), TerminalState, }