-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async and sync in the same program #6
Comments
Ruyt 1.51 introduce a new resolver in cargo, which will compile crate for each set of different feature gates on dependencies. With this v2 resolver, this issue is automatically solved. Just add the following lines in your [package]
resolver = "2"
# Or if you're using a workspace
[workspace]
resolver = "2" Plus, it doesn't make sense to compile both async and sync in the same program. Why use a blocking code when you can use async version? |
Building on what @sunfishcode wrote, I have a trait in one of my crates (sorry, source is not public at this time) that is used in another of my programs in both sync (local file system) and async (contacting a remote service) contexts. For the local file system part of my program, I'd really rather not take on the complexity of async. Please reopen and reconsider. |
I think the new resolver can help when one of dependencies use a different feature(blocking or async). I haven't tested it yet, but I think it should work. And regarding the case of providing async and blocking at the same time, I don't really get it. When the program is able to run async code, it is natural to stick with async implementation. And when async executor is not available, it's ok to use blocking version as a fallback. I have actually tried writing a [new procedural macro] |
Unfortunately no. The recommendation in the documentation is wrong:
Because the resolver v2 only fixes the conflicts in very specific cases (https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2), none of which match this one:
I've also made a repo to try it myself: https://github.com/marioortizmanero/resolver-v2-conflict We're trying to fix this at rspotify (ramsayleung/rspotify#222) and we've ended up at the same conclusion. |
Also, I think it might be best to expand with modules: #[maybe_async::both]
async fn foo() -> bool {
true
} to this: #[cfg(feature = "async")]
mod impl_async {
pub async fn foo() -> bool {
true
}
}
#[cfg(feature = "sync")]
mod impl_sync {
pub fn foo() -> bool {
true
}
} So that it's just easier to use and more flexible overall. Though the only way I can think of to make it work without duplicate module names is something like: maybe_async! { // should only be called once per namespace
async fn a() {}
async fn b() {}
} Which would generate: mod impl_sync {
fn a() {}
fn b() {}
}
mod impl_async {
async fn a() {}
async fn b() {}
} At the cost of slightly changing the semantics of this macro. Not sure if it's worth it. |
It should also be the default behavior because the current one is just wrong; no need to use |
After attempting to implement this I've come to the conclusion that it's far from trivial (I kinda saw it coming).
TLDR: processing functions in the macro works well because it's as easy as adding the suffix So yeah, I'm stuck. The only way to do this may be appending |
@fMeow, can you add a warning about this library to the docs until this is worked on? |
Sure, I will update the doc in both readme and documentation in crate.io in the next minor release. |
Update: we can actually do the duplicate traits thing if we also duplicate the struct that implements it. So instead of having a single struct that implements both the async and the sync traits, which is ambiguous, we have two structs, one for each trait. I've got it working more or less in my fork, but I only modified the
So basically,
We could later add parameters to configure the suffix ( What do you think? Do you like it? Is it enough for everyone's use cases? I know it would for |
Thanks a lot for this library. Quite cool idea!
imo that is too broad: there are valid use-cases to offer both versions. E.g. 1: a user of a library may need to consume data from two databases on which one offers a native E.g. 2: when loading data from large files that still fit in memory, it is almost always faster to download the file Imo these decisions could be left to the users of this library. |
I think it would be fine to append |
This is super interesting and might actually fix our issue! https://blog.rust-lang.org/inside-rust/2022/07/27/keyword-generics.html |
I have a experimental inplementation to enable both async and sync, where I try to add a proc macro on a rust module to compile the underlying module to two modules, one for synchronous code and another for async. I have got an working implementation a long time ago, but I never publish it because error messages from rustc is quite messy. I wonder there is some new feature in rust that can tackle the messy error message issue. Might look into it this week. |
Is there an update on this? :) |
Going back to the other suggestion, why not use the I think that would be more in the direction of the common pattern of having a |
FWIW I wrote a crate that addresses exactly (and only) this problem: https://crates.io/crates/async-generic |
@scouten-adobe That looks really cool! But it does not solve the problem of having a single implementation, right? I mean, I think the ideal solution would be implement once and generate a sync and an async version. Also, your crate seems to be incompatible with |
@janosimas the intent of There's no intentional incompatibility with |
(Note: @scouten-adobe and I are the same person, just writing from work or personal computers.) |
@scouten maybe I was not clear with what I meant. Seems to me that to use The incompatibility is that if I mark a function with I can open a discussion in |
@janosimas I'd welcome any suggestions on how to address/resolve if you have any. (Maybe those should be filed as an issue over in |
@scouten Sorry, I got busy and didn't follow up. A colleague came with a solution using the #[duplicate_item(
module_type maybe_async_attr some_other_type;
[ non_blocking ] [ must_be_async ] [ AsyncType ];
[ blocking ] [ must_be_sync ] [ Blockingtype ];
)]
mod module_type {
#[maybe_async_attr]
pub async fn foo(input: some_other_type) -> ... {
...
}
} |
I maintain some libraries that have sync and async users. The async users wrap calls in
spawn_blocking
, which works, but isn't ergonomic and may be suboptimal.maybe_async
looks like it could potentially be (part of) an alternative approach.If I read the docs right,
maybe_async
can support both sync and async users, but by using a cargo feature, it can't support both in the same program. If the library gets used in multiple places in the same program, and one of them enablesis_sync
, it would break the other places that need async.It seems like it would be useful to have a macro that expands to both sync and async, using different names, so that they can coexist. For example,
might expand to:
so that users can call whichever form they need by name (though I'm not attached to the particular naming convention here).
The text was updated successfully, but these errors were encountered: