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

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

Closed
gabbifish opened this issue Mar 6, 2024 · 4 comments
Closed

Comments

@gabbifish
Copy link
Contributor

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
Copy link
Contributor Author

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

@chinedufn
Copy link
Owner

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.

/// Test code generation for Rust function that takes an owned String argument.
mod extern_rust_fn_with_owned_string_argument {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod foo {
extern "Rust" {
fn some_function (arg: String);
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
#[export_name = "__swift_bridge__$some_function"]
pub extern "C" fn __swift_bridge__some_function(
arg: *mut swift_bridge::string::RustString
) {
super::some_function(unsafe { Box::from_raw(arg).0 })
}
})
}


I'll PR a fix tonight.

@chinedufn
Copy link
Owner

Thanks again for reporting this.

The warning was fixed here #254

@gabbifish
Copy link
Contributor Author

Thank you so much!!

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