Open aiongg opened 1 year ago
Thanks for the detailed issue and examples.
I'm not understanding what you mean by Option 2? Can you illustrate what you mean with code like you did for Option 1?
If possible, can you also include a snippet of the code that is using the *mut *mut u8
so that I can better understand your use case?
I don't have code for Option 2 since I couldn't figure it out, but it would basically be the rust function returning a VecData
in Swift. Here is the code right now (without swift-bridge), the files aren't very much longer than the code snippets if I were to paste them here, so I figured it's easier just to link directly:
https://github.com/aiongg/khiin-rs/blob/master/swift/EngineBindings/src/lib.rs
https://github.com/aiongg/khiin-rs/blob/master/swift/EngineBindings/EngineController.swift
https://github.com/aiongg/khiin-rs/blob/master/swift/EngineBindings/generated/khiin_swift.h
Thanks for the links
I don't have code for Option 2 since I couldn't figure it out
Can you write a snippet showing, roughly, how you would want the bridge module signature to look?
It may sound unnecessary, but I'm asking because I've found that talking in terms of bridge modules reduces cognitive load substantially and gets us on the exact same page.
i.e. something like:
// Rust
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
// ... the signature that you want to support
}
}
that I could somehow load into a Data in Swift
Gotcha. It looks like Data
's initializer methods just copy bytes from a pointer and length https://developer.apple.com/documentation/foundation/data/1780158-init
So, if you had a way to get the pointer to the first byte in the RustVec<UInt8>
(you can already get the length with .len()
) would that be enough for you to construct your Data
?
(Sorry I first posted from another device where I was logged in to a different account.)
Sure, well the ideal signature would be bytes-in-bytes-out:
fn send_command(command_bytes: &[u8]) -> Option<Vec<u8>>;
with no length counting or in-out params at all. This is identical to the actual method I use in the cross-platform library, seen here: https://github.com/aiongg/khiin-rs/blob/master/khiin/src/engine.rs#L51
So in that case my FFI code would be one-liner, which would be great.
As I mentioned I'm new to Swift so I'm not positive, but it seems like the pointer and length would be sufficient for a Data
.
This would make it very straightforward for anyone else using protobuf to pass arbitrary data between rust and swift without any additional setup, since protobuf is just a serializer/deserializer to bytes.
Ok, cool.
In that case, it looks like the signature that you want is already supported, so it sounds like the solution here is to expose a method to get the pointer to the first item in a Vec<T>
.
We can add a static func VecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer<Self>
method to the Vectorizable
protocol.
Then we can expose this behind RustVec<T>.as_ptr
We can add a testVecU8AsPtr
test where we
RustVec<UInt8>
10
vec.as_ptr()
10
Here's an example Vec
as_ptr
We can add the implementation for as_ptr
near here
and here
Here's how to run the tests to confirm that the new tests pass:
cargo test --all && ./test-swift-rust-integration.sh
Wow awesome. I would be up for giving this a shot.
One thing I didn't get on the first read of your answer was how to deal with the &[u8]
, since the README says &[T]
is not yet implemented?
By the way it looks like we already have as_ptr
implemented here:
I am using protobuf to pass data back and forth between a cross-platform library and client apps on various platforms. I have successfully gotten my code to work without swift-bridge, but I thought I'd give it a go since it would be much easier to automatically generate the Swift Package, and potentially make my code a bit cleaner.
For protobuf, the only data that I pass around is a bucket of bytes. I suppose there are two options:
Vec<u8>
from swift as aRustVec<UInt8>
, but but then I don't know how to convert that into aData
in Swift for passing back to protobuf.For option 1, the generated Swift code appears to be wrong:
In my manual version, I called the function using
&UnsafeMutablePointer<UInt8>&
, notUnsafeMutablePointer<UnsafeMutablePointer<UInt8>>
.I'm pretty new to Swift, so I'm not sure what all the nuances of these things are, this is just what worked / what didn't work for me.
For reference, here is the working (non swift-bridge) call site corresponding to the same Rust signature as above:
(Edit: formatting)