chinedufn / swift-bridge

swift-bridge facilitates Rust and Swift interop.
https://chinedufn.github.io/swift-bridge
Apache License 2.0
842 stars 62 forks source link

How to return `Vec<u8>` from Swift to Rust #228

Closed timwedde closed 1 year ago

timwedde commented 1 year ago

I've been struggling with this for a while now, so now I'm reaching out for some help as I'm seemingly unable to figure it out by myself.

What I want to do is effectively the inverse of https://github.com/chinedufn/swift-bridge/issues/214

My Rust code will call into Swift with a couple parameters and once it completes, the Swift code should return a Vec<u8> of bytes back to my Rust code.

MWE is reasonably simple:

// Rust
fn main() {
    let vec = ffi:get_bytes();
    println!("vec: {:?}", vec);
}

#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        fn get_bytes() -> Vec<u8>;
    }
}
// Swift
func get_bytes() -> RustVec<UInt8> {
    let vec = RustVec<UInt8>()
    // do some work to fill the Vec here
    return vec
}

This is what the codegen outputs:

// Swift
@_cdecl("__swift_bridge__$get_bytes")
func __swift_bridge__get_bytes () -> RustVec<UInt8> {
    { let val = get_bytes(); val.isOwned = false; return val.ptr }()
}

During compilation, this then throws an error at me: error: cannot convert value of type 'UnsafeMutableRawPointer' to closure result type 'RustVec<UInt8>

Which makes sense, given that it returns val.ptr, which is not of type RustVec<UInt8>.

I'm very new to pointer wrangling and working with lower level stuff in between Rust/Swift, so there may already be a solution out there, however I was not able to find a working way forward. I already tried some pointer shenanigans, but the codegen always seems to output return types that don't fit whatever I can give it, so I'm a little stuck.

TL;DR: How do I return a bucket of bytes from Swift to Rust?

chinedufn commented 1 year ago

Thanks a lot for the detailed issue.

Looks like this just isn't implemented.

The generated signature just needs to be changed from:

// Swift (WRONG)
@_cdecl("__swift_bridge__$get_bytes")
func __swift_bridge__get_bytes () -> RustVec<UInt8> {
    { let val = get_bytes(); val.isOwned = false; return val.ptr }()
}

to:

// Swift (CORRECT)
@_cdecl("__swift_bridge__$get_bytes")
func __swift_bridge__get_bytes () -> UnsafeMutableRawPointer {
    { let val = get_bytes(); val.isOwned = false; return val.ptr }()
}

Here's how to implement support for returning Vec<u8> from Swift if you of anyone else is interested.

Implementation Guide

Contributing Guide

First, check out the contributing guide for quick tips on how to contribute support for a new signature https://github.com/chinedufn/swift-bridge/blob/ecd3b974754e32fc951b8bc4098ecef84e974821/book/src/contributing/adding-support-for-a-signature/README.md

Adding a codegen test

Add a codegen test for returning a Vec<u8> from an extern "Swift" function.

Here's one to use as inspiration: https://github.com/chinedufn/swift-bridge/blob/7e140ca5e6ec3f969c19001c844f98fe76bd787c/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs#L230-L284

We want the generated Swift function to return an UnsafeMutableRawPointer instead of RustVec<UInt8>.

Integration Test

Next, add an integration test for returning a Vec<u8> from an extern "Swift" function near here:

https://github.com/chinedufn/swift-bridge/blob/c1fd7177b5d3a8918e34b6bc96ae29437cfc1487/crates/swift-integration-tests/src/vec.rs#L1-L28

Here's an example of testing extern "Swift" functions that return Strings https://github.com/chinedufn/swift-bridge/blob/bb46ca6e7d6e421b91dfff9a53d085450f854c03/crates/swift-integration-tests/src/string.rs#L9-L18

Passing Tests

Change this to return UnsafeMutableRawPointer if the host lang is Swift and the type_pos is TypePosition::FnReturn https://github.com/chinedufn/swift-bridge/blob/de059024732fa2f8135a9fe1232b727d96794af6/crates/swift-bridge-ir/src/bridged_type.rs#L1149-L1151

Here's an example of conditionally determining the Swift type to use: https://github.com/chinedufn/swift-bridge/blob/de059024732fa2f8135a9fe1232b727d96794af6/crates/swift-bridge-ir/src/bridged_type.rs#L1135-L1148

timwedde commented 1 year ago

Nice, thanks a ton for the detailed pointers (pun intended)! I will probably give this a whirl to produce a proper implementation for this, I'll send a PR your way some time this week, probably.