chinedufn / swift-bridge

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

warning: `extern` block uses type `RustString`, which is not FFI-safe #253

Closed gabbifish closed 6 months ago

gabbifish commented 6 months ago

I exposed a transparent struct in Rust, which is used by a swift function exposed back to rust. This produces the error

warning: `extern` block uses type `RustString`, which is not FFI-safe

Seems this is related to RustStrings being used in a Swift function exposed to Rust, and the solution looks as simple as

diff --git a/src/std_bridge/string.rs b/src/std_bridge/string.rs
index 387e5e5..3823aac 100644
--- a/src/std_bridge/string.rs
+++ b/src/std_bridge/string.rs
@@ -22,6 +22,7 @@ mod ffi {
 }

 #[doc(hidden)]
+#[repr(C)]
 pub struct RustString(pub String);

Example code to reproduce level (rust):

#[swift_bridge::bridge]
pub mod ffi {
 #[swift_bridge(swift_repr = "struct")]
 pub struct ForwardedLog {
 message: String,
 // other fields omitted
 }

 extern "Swift" {
 type SwiftLogger;
 fn log(&self, log: ForwardedLog);
 }
}

and on the swift side:

public final class SwiftLogger: Sendable {
 public func log(log: ForwardedLog) {...}
}

(code is provided under the MIT license)

gabbifish commented 6 months ago

If the proposed fix above looks good, I'd be happy to open the PR for it!

chinedufn commented 6 months ago

Thanks for reporting this and including steps to reproduce.


We don't want RustString(String) to be #[repr(C)] since std::string::String isn't FFI safe.

For example, the following program:

fn main() {
}

#[repr(C)]
pub struct RustString(String);

pub extern "C" fn return_rust_string() -> RustString {
    unimplemented!()
}

Would print the following in stderr when compiled:

   Compiling playground v0.0.1 (/playground)
warning: `extern` fn uses type `String`, which is not FFI-safe
 --> src/main.rs:8:43
  |
8 | pub extern "C" fn return_rust_string() -> RustString {
  |                                           ^^^^^^^^^^ not FFI-safe
  |
  = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
  = note: this struct has unspecified layout
  = note: `#[warn(improper_ctypes_definitions)]` on by default

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f7a92eea3a4ba727ab22536b99a610dc


Fortunately, we don't actually pass the RustString across the FFI boundary.

Instead, we pass a pointer to the RustString, which is totally FFI safe.

https://github.com/chinedufn/swift-bridge/blob/27246447b4ac741b8bf086d3dfca15b47558e44f/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs#L5-L28


I'll PR a fix tonight.

chinedufn commented 6 months ago

Thanks again for reporting this.

The warning was fixed here https://github.com/chinedufn/swift-bridge/pull/254

gabbifish commented 6 months ago

Thank you so much!!