Summarize the problem
Hi all, the problem I am facing is that I am unaware how to simulate an async function through rust FFI.
I am writing a program in rust, and needed some native MacOS functions, I wrote the functions in the swift programming language using and made them public facing ie
@_cdecl("fooExported")
public func fooExported()
this works well using the swift-rs crate as it also safely converts some swift types into rust primitives. However, one of the things I am trying to accomplish is done in an async function which I cannot seem to get working as async functions (or methods idk), cannot be exposed using @_cdecl
see [#Enlightenment] for the "real" issue
Describe what you’ve tried
I did some reading and found an answer on the swift-rs issues page: which was quite helpful in getting me started, but quite frankly, my knowledge of rust is superficial, and worse in swift, so I was unsure about what was actually going on under the hood with async conversion etc.
Essentially, what I have now is the following:
//consider an async function with arg and return types
func foo(arg: Int) async -> String {
arg.description
}
//we need a function signature that works in the C ABI
@_cdecl("fooExported")
public func fooExported(arg: Int, context: UInt64, completion: @Sendable @convention(c)(SRString,UInt64) -> ()) {
Task {
let r = await foo(arg: arg)
completion(SRString(r), context)
}
}
as shown in the issue example, and on the rust side:
#[tokio::main]
async fn main() {
let gg = foo(1738).await;
println!("{gg}");
}
async fn foo(arg: i64) -> String {
let (sender,receiver) = continuation();
let boxed_sender = Box::new(sender);
unsafe {
fooExported(1337, Box::into_raw(boxed_sender) as u64, completion)
}
receiver.await
}
extern "C" fn completion(string: SRString, context: u64) {
let unbox = unsafe {Box::from_raw(context as *mut Sender<String>)};
unbox.send(string.to_string());
}
which seems to cause no issues other than the fact that fooExported is obviously not in scope... my issue with it further is that it has a weird looking function signature that I am unfamiliar with.
I have tried the following functions:
swift!(fn fooExported(arg: i64, context: u64, completion: extern "C" fn(SRString, u64)));
swift!(fn fooExported(arg: i64, context: u64, completion: fn(SRString, u64)));
unsafe fn fooExported(arg:i64,context:u64,completion:extern "C" fn(SRString,u64)){
extern "C" {
fn fooExported(arg: <i64 as swift_rs::SwiftArg>::ArgType,context: <u64 as swift_rs::SwiftArg>::ArgType,completion: <extern "C" fn(SRString,u64)as swift_rs::SwiftArg>::ArgType);
}let res = {
let arg = swift_rs::SwiftArg::as_arg(&arg);
let context = swift_rs::SwiftArg::as_arg(&context);
let completion = swift_rs::SwiftArg::as_arg(&completion);
fooExported(arg,context,completion)
};
swift_rs::SwiftRet::retain(&res);
res
}
unsafe fn fooExported(arg: i64, context: u64, completion: fn(SRString, u64)) {
unsafe extern "C" {
fn fooExported(
arg: <i64 as swift_rs::SwiftArg>::ArgType,
context: <u64 as swift_rs::SwiftArg>::ArgType,
completion: <fn(SRString, u64) as swift_rs::SwiftArg>::ArgType,
);
}
let res = {
let arg = swift_rs::SwiftArg::as_arg(&arg);
let context = swift_rs::SwiftArg::as_arg(&context);
let completion = swift_rs::SwiftArg::as_arg(&completion);
fooExported(arg, context, completion)
};
swift_rs::SwiftRet::retain(&res);
res
}
Englightenment
Okay, reading through the inlined macros (last two examples) I figured that the issue is here: completion: <fn(SRString, u64) as swift_rs::SwiftArg>::ArgType, -- trying to cast a fn into a swift type ?
The aforementioned example on the github issue got it to work, I just dont know how
The times I did get something to run by just removing the type conversion it crashed with the following error:
Failed to look up symbolic reference at 0x1048e194d - offset 288547 - symbol symbolic _____Sg ScP in /target/debug/app
zsh: abort cargo run```
(reason i tagged it with swift as well is because i copy pasted the swift code, maybe there is a better way to approach this?)