Skip to content
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

Asynchronous functions / callback support #31

Open
caoimhebyrne opened this issue Mar 7, 2023 · 3 comments
Open

Asynchronous functions / callback support #31

caoimhebyrne opened this issue Mar 7, 2023 · 3 comments

Comments

@caoimhebyrne
Copy link
Contributor

It would be amazing if we could call asynchronous Swift functions from Rust.

The only problem is: _@cdecl doesn't allow us to mark our functions as asynchronous:

src-swift/lib.swift

@_cdecl("my_async_fn") // ERROR: _@cdecl global function cannot be asynchronous
func myAsyncFunc() async {
    print("Hello, world!")
}

Since that's not possible, it would be nice if we could have better support for closures:

src-swift/lib.swift

@_cdecl("my_closure_func")
func myClosureFunc(closure: @escaping @convention(c) () -> Void) {
    closure()
}

src/main.rs

use swift_rs::{swift, SRClosure};

swift!(pub(crate) fn my_closure_func(SRClosure<Void>));

fn main() {
    let closure: SRClosure<Void> = || {
        println!("Hello!!");
    }.into();

    my_closure_func(closure);
}

The Rust code above is just a proof-of-concept, and I'm not sure if everything I described there is possible, since we can't just pass a pointer to the callback around (from my understanding at least). We may have to implement something like this C-callbacks example from the Rustonomicon (with some sort of Swift code generator?)

@caoimhebyrne
Copy link
Contributor Author

For some extra context, I implemented a workaround in one of my own crates for calling methods with closures, but it's messy and not ideal:

@_cdecl("my_func")
func myFunc() -> String {
    let semaphore = DispatchSemaphore(value: 0)
    var returnValue = ""

    // getStringValue is not real, but has a signature something like this:
    // func getStringValue(closure: @escaping (String) -> Void)
    getStringValue() { (value) in   
        returnValue = value
        semaphore.signal()
    }

    semaphore.wait()
    return value
}

Being able to use closures in Rust directly, or even better, do some magic to allow asynchronous functions, would be better than this approach.

@Brendonovich
Copy link
Owner

Brendonovich commented Mar 7, 2023

since we can't just pass a pointer to the callback around

I think you'd be surprised 😉

SRClosure is a really smart idea and may actually work, since @lucasfernog already did something similar for Tauri

type PluginMessageCallbackFn = unsafe extern "C" fn(c_int, c_int, *const c_char);
pub struct PluginMessageCallback(pub PluginMessageCallbackFn);

impl<'a> SwiftArg<'a> for PluginMessageCallback {
  type ArgType = PluginMessageCallbackFn;

  unsafe fn as_arg(&'a self) -> Self::ArgType {
    self.0
  }
}

I could even try an SRFuture that gets instantiated with an async block in Swift and converts to a Future in Rust, but I'm not 100% confident about that. In theory it'd just be a matter of handling success + failure closures.

Duplicate of #29, but I'll keep this open since it's more detailed.

@caoimhebyrne
Copy link
Contributor Author

I could even try an SRFuture that gets instantiated with an async block in Swift and converts to a Future in Rust, but I'm not 100% confident about that. In theory it'd just be a matter of handling success + failure closures.

That would be amazing, I would love to contribute but this level of Rust FFI is out of my league :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants