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

Support bridging Vec<TransparentStruct> #305

Open
ryankaplan opened this issue Dec 3, 2024 · 1 comment
Open

Support bridging Vec<TransparentStruct> #305

ryankaplan opened this issue Dec 3, 2024 · 1 comment

Comments

@ryankaplan
Copy link

ryankaplan commented Dec 3, 2024

Hi again :)

I'm running into an issue and wonder if I'm missing something or if this is by design. I have an array of transparent structs that I'd like to return to Swift. I'd be fine with returning a RustVec, but eventually I want a Swift array of FfiThings structs.

Right now my setup looks like this...

// Thing is a Rust struct that I also expose to wasm, so I'd prefer
// not to move it into `mod ffi`.
impl From<Thing> for ffi::FfiThing {
  ...
}

fn clone_and_into(layers: Vec<Layer>) -> Vec<ffi::FfiLayer> {
    layers.into_iter().map(|l| l.into()).collect()
}

#[swift_bridge::bridge]
mod ffi {
    #[swift_bridge(swift_repr = "struct")]
    struct FfiThing {
       ... simple properties ...
    }

    extern "Rust" {
        // `clone_and_into` is necessary because without it,
        // swift-bridge says that Vec isn't clone-able.
        #[swift_bridge(return_with = clone_and_into)]
        fn get_things() -> Vec<FfiThing>;
    }
}

While the above works fine from a Rust perspective... when I compile Swift the return type is RustVec which doesn't compile because FfiThing isn't vectorizeable. This makes sense to me, since RustVecs are shallow views into Rust, and I want to copy FfiThing over to Swift.

Do you have a recommended path here? Is there a way, for example, to tell the swift-bridge that I want an Array in Swift and not a RustVec? The array and structs in question will be read frequently from Swift, so I really do just want to copy all the data over to Swift rather than repeatedly call into Rust.

@chinedufn
Copy link
Owner

We haven't implemented support for bridging Vec<TransparentStruct> yet.

Here's where we support Vec<TransparentEnum> when we know that the TransparentEnum can safely be copied (because the enum it doesn't contain any variants).

let vec_support = if shared_enum.has_one_or_more_variants_with_data() {
// Enums with variants that contain data are not yet supported.
quote! {}
} else {
generate_vec_of_transparent_enum_functions(&shared_enum)
};


The reason that being able to copy the T in Vec<T> is important for transparent enums/structs is that, currently, we don't currently support shared references to TransparentStruct or TransparentEnum.

For instance, the the Swift and Rust side can't currently share instances of Foo and Bar below by reference. We only support passing owned Tranparent{Struct,Enum} over the FFI boundary.

#[swift_bridge::bridge]
mod ffi {
    struct Foo { inner: String }
    enum Bar { Variant(String) }
}

The reason that we would need support for passing references to Trans{Struct,Enum} is that if you have Vec<Foo> and pass that from Rust to Swift, then you can't have the Swift side do the equivalent of let foo = &vec_of_foos[2], because we can't currently take a reference to Foo.

Without support for referencing elements in the vec then all you can do is add or remove elements to the vec.

That would actually be useful in your case, since all you want to do is remove all the elements from the RustVec and push them into a Swift Array.

But, a complete design will require supporting methods like Vec::get, which return a reference to the element (i.e. Option<&Foo>.


So, full support of bridging vecs that contained Transparent{Struct,Enum} that contained data would require us to support passing around references to Transparent{Struct,Enum}.


We have not yet explored sharing references to Transparent{Struct,Enum} between Rust/Swift.

When we explore and solve that (assuming there's a good solution) that'll unlock Vec<Struct> support.


Swift 6 has a lot of new ownership features, so there may be good ways to support sharing references to Transparent{Struct,Enum} between Rust and Swift while still letting both of them have direct access to the inner fields (i.e. we might be able to guarantee that both sides only have read access, meaning both sides can safely share a reference).

I don't know whether this is possible. I'll have to study some of the concurrency related Swift evolution documents to figure out the best path forward, if there is one. https://github.com/swiftlang/swift-evolution

You may wish to subscribe to #155 which tracks our work on Rust+Swift ownership.


Do you have a recommended path here?

One temporary solution would be to pass your struct as an opaque type. We support bridging Vec<OpaqueRustType> currently.

#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        struct Thing;

        fn get_things() -> Vec<Thing>;
    }
}

@chinedufn chinedufn changed the title Are Vec<Struct> return types supported? Support bridging Vec<TransparentStruct> Jan 30, 2025
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