[Breaking] Remove Async trait and Send bounds in async functions#149
Merged
soareschen merged 8 commits intomainfrom Sep 12, 2025
Merged
[Breaking] Remove Async trait and Send bounds in async functions#149soareschen merged 8 commits intomainfrom
Async trait and Send bounds in async functions#149soareschen merged 8 commits intomainfrom
Conversation
Async trait and Send bounds in async functionsAsync trait and Send bounds in async functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR removes the
Sendbound in theFuturereturned from the async functions desugared from the#[async_trait]macro. And along with it, thecgp-asynctrait and its constructs includingAsync,MaybeSend, andMaybeSync.Background
The original motivation of the
cgp-asynccrate was two fold. First, it was belived thatSendandSyncbounds need to be added everywhere in the code, in order to make theFuturereturned from async functions to implementSend. This requirement is important, because it is common that users want to call some async functions within spawners liketokio::spawn, which requires theFutureto implementSend.Another motivation was to experiment on supporting async-generic on context-generic code, so that they could be used inside sync code by stripping away all use of the
asyncand.awaitkeywords. This was done by adding anasyncfeature flag incgp-async, which would turn into sync mode and strip all use of async when the feature flag is disabled.Problems
The main problem with the current approach is that it adds a lot of noise and complexity to async code. In order to use an abstract type in an async function, it often has to require the
SendandSyncbounds to be present.Furthermore, the feature flag approach for async-generic don't really work that well, as the
asyncfeature flag can easily be activated by default within nested dependencies. This means that almost every async-generic crate would have to adddefault-features = falseand an additionalasyncfeature in order to propagate the control.New Approach
With this PR, a new approach is adopted. Instead of annotating
SendandSyncbounds everywhere, we just stop requiring the futures returned to implementSend.Despite this, a workaround is available to gain back the
Sendbound of a top-level future, in case if we need to use it withintokio::spawn. This is done by writing context-specific implementation of theSendRunnertrait on the concrete context, which forwards the actual implementation toRunner. This works because when the concrete types are accessible, Rust would be able to infer that theSendbound is satisfied on the concrete future. An example code that demonstrates this workaround is available in the test code.Furthermore, the
Sendbound will soon become unnecessary altogether once the return type notation (RTN) feature becomes stabilized in Rust rust-lang/rust#109417. After that, we won't need to implement wrappers on the concrete context to resurface theSendbound.As for async-generic, the feature is not really used, and so it will be removed altogether. Instead, we will rely on runtime assertion via the call to
futures::block_onto assert that an async function don't really perform any async operation and could return immediately.In the future, it would also be possible to develop type-level DSLs similar to Hypershell, together with handler traits like
ComputerandAsyncComputerto define async-generic programs in teh DSL.Breaking Change
The removal of
cgp-asyncis a major breaking change. It would affect all existing CGP code that uses traits likeAsync. However, this breaking change is necessary in order to bring a simpler foundation of CGP for future code. Furthermore, it is better to make this breaking change early while there are still not too much CGP code written.Here is a summary of the breaking changes:
cgp-asynccrate is removed.Async,MaybeSend,MaybeSyncare removed.HasAsyncErrorType,CanRaiseAsyncError,CanWrapAsyncErrorare removed.Codeparameter is added to theCanRuntrait.In most cases, you should be able to migrate to the new version by removing all bounds to to
Async. Alternatively, you could copy over the original definitions of alias traits likeAsyncandHasAsyncErrorType, so that you can continue using them locally in your project.