is it possible to return handlers to complex go structs
Hi,
I am wondering if and how it is possible to return complex structures (or points to them more likely) as opaque types from calls.
Say I have a client implemented in go and want to initialize it and then return a handler to the client.
I basically want to do something like this:
struct FancyGoClient {
client: NonNullPtr<()> // or some other struct
}
impl FancyGoClient {
fn new(url: &str) -> Self {
Self{
client: call_to_go_init(url)
}
}
fn do_the_thing(&self) -> u64 {
call_to_go_do_the_thing(self.client)
}
}
In the long run, I plan to split up a bunch of coupled components in this repository, including exposing different ABI interfaces for selection, using interfaces for structure conversion and allowing users to implement them manually, etc. In the short term, to solve your needs, you can manually implement a wrapper on the golang side to convert the structure again.
Manuall is totally fine, the problem I ran into is that in the rust-side of things I couldn't express "this is a pointer to unknown" as any approach I tried told me parameterized type are a no-no
I have the same exact usecase and I can't find a way to express pointers in the FFI boundary.
Tried Box, got Error("custom types with arguments are not supported").
Tried *const got only path type returns are supported.
Tried unsafe usize to pointer conversions on both sides and got incorrect values because of possible wrong memory alignments.
I skimmed the blog post about the library and it felt like this is expected because it explicitly mentions "...As we cannot control the lifetime of the return value Rust side receives (the upper layers might even store it in a global structure), the copy upon being called by FFI on the Rust side is inevitable; Rust performs the copy after being called, allocating its own memory..." somewhere. Does that mean we can't work with raw pointers in the FFI boundary? Do we need to have owned values or smart pointers in the return values of Go functions?
Does that mean we can't work with raw pointers in the FFI boundary?
Passing pointer as a number is ok now. But passing and managing the object lifetime behind the pointer are 2 different things.
Make a smart pointer and map it to different languages are good, at lease for Rust/C++. However, golang seems haven't provide a destructor ability which can be automatically called when the object is going to be released.
There are another totally different way to do that: since I also provide a shared-memory mode, doing it based on communication will be very easy. Current definition is like:
- Caller->Callee: Request
- Callee->Caller: Response+RequestReleaseNotify
- Caller->Callee: ResponseReleaseNotify When passing pointers manually, we can hold the object until receive ResponseReleaseNotify at golang side.
However, now the CGO mode and shared-memory mode are compatiable with each other. If implement it like the former solution, it will only work under shared-memory mode.
To make CGO mode also work, we may add another CGO call for each async call that does not copy response instantly. This will make the project more elegant by making some abstraction with the oneway message passing(it can be a CGO call or shm queue message).
Do you have any idea about it? Or do you have interest implementing it? :)