Taaitaaiger / jlrs

Julia bindings for Rust
MIT License
406 stars 22 forks source link

Exporting same Foreign Type in two different Libraries #134

Open ju6ge opened 2 weeks ago

ju6ge commented 2 weeks ago

Consider the following Situation:

I have a rust-crate and corresponding JuliaPackage that use jlrs lets call this package jlrs-foo it exports an OpaqueType.

crate julia-package
jlrs-foo JlrsFoo
just-type julia-type
jlrs_foo::Foo JlrsFoo.Foo

Now there is a second jlrs Package that depends on jlrs-foo, it has its own types, but also wants to have some function that actually return the type Foo from jlrs-foo. Lets call this package jlrs-bar

Here is what I have tried jlrs-bar/src/lib.rs:

use jlrs::prelude::*;
use jlrs_foo::Foo;

julia_module! {
    struct Foo;

    fn get_foo() -> TypedValueRet<Foo> as jlrsbar_get_foo();
}

But this leads to the situation that I now have two forein types that are from rusts perspective the same type, but not from Julias perspective:

just-type julia-type
jlrs_foo::Foo JlrsFoo.Foo
jlrs_foo:Foo JlrsBar.Foo

Since Julia is strongly typed I can not just convert one into the other. I have tried to do that a few different ways, always ending up in a julia error. This is annoying, because I can not just use the value of jlrsbar_get_foo like and other value of type Foo when it is constructed in JlrsFoo.

I have also tried not exporting the type in jlrs-bar and instead try to return ValueRet instead of TypedValueRef<Foo>, but this just leads me down into core dump situations.

Any Ideas how to solve/deal with this Situation?

Taaitaaiger commented 2 weeks ago

That's not possible yet, unfortunately. The main challenge I see is that it can't be truly guaranteed they'll be the same types on the Rust side either; even if they both use the same version of a crate, the libraries can be built with different versions of Rust which can cause issues due to ABI incompatibilities.

I'm not sure what the best solution is. For now I would recommend either writing a single library, or sticking with extern "C" functions and fully repr(C) types if you really need to share them between different libraries.

ju6ge commented 1 week ago

Hm that is indeed sad. But this is a hard problem. Regarding the different Rust versions problem, wouldn't requiring repr(C) for all exported types solve this problem? Then the rust version should not matter for the binary structure of the type that is being exported? Using compatible versions of the crates is still on the user then.

Anyway I will try to avoid this situation for now using a different approach, sticking to only one crate is not an option. So I will need to do some sort of serialization/deserialization.

Taaitaaiger commented 1 week ago

Yeah, I was thinking along similar lines. The main issue I see is that every exposed type would need to be repr(C), and the entire public API extern "C". It would not be possible to write a repr(C) newtype wrapper around Vec, for example, and access that array from independent libraries.