7

I want to pass in a String to a Rust lib, but it always throws a segmentation fault.

Here's the code:

 // lib.rs
 #[no_mangle]
 pub extern fn process(foo: String) -> String {
     foo
 }

And the Ruby file:

 # embed.rb
 require 'ffi'

 module Hello
   extend FFI::Library
   ffi_lib 'target/release/libembed.dylib'
   attach_function :process, [ :string ], :string
 end

 puts Hello.process("foo")
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
xijo
  • 4,366
  • 4
  • 23
  • 30

2 Answers2

6

Disclaimer: I've never used Ruby-FFI before; I'm going on what I can find in the documentation.

According to the Ruby-FFI wiki page on types, :string is equivalent to a NUL-terminated C string. This is not the same as a Rust String. A String in Rust is (presently) three times larger!

The corresponding type in Rust would be *const ::libc::c_char. Of note, there is also std::ffi::CString, which is designed for creating C strings, and std::ffi::CStr which is the safe wrapper type which can be created from either a CString or a *const c_char. Note that neither of these is compatible with *const c_char!

In summary, to deal with C strings in Rust, you're going to have to juggle the types. Also keep in mind that, depending on what you're actually trying to do, you may need to also deal with manually managing memory using libc::malloc and libc::free.

This answer to "Rust FFI C string handling" gives more details on how to deal with C strings in Rust. Although the context for the question is integrating with C code, it should be equally useful in your case.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • 3
    "A String in Rust is (presently) three times larger!" is technically true, in the sense that a String is a pointer and two integers and a `char *` is one pointer. But that doesn't mean that "lolfoobarthisisastring" is three times larger in Rust than in C. – Steve Klabnik May 25 '15 at 17:40
  • Thanks, your answer pointed me in the right direction. I stumbled over https://github.com/vrinek/ruby-rust-examples/tree/master/messages. Although I had to adjust the example to the new rust version I could get it to work. – xijo May 25 '15 at 18:34
  • Funny side note: my rust implementation is way slower than my pure ruby. Damn learning curve :D – xijo May 25 '15 at 18:34
  • @Joe are you compiling with optimizations? That bites every newbie! – Shepmaster May 25 '15 at 19:27
  • It's currently my belief that you [can't easily return allocated strings](https://github.com/rust-lang/rust/pull/25777) via FFI. You could allocate them using `malloc` and `free` I suppose though. – Shepmaster May 25 '15 at 19:29
  • @Shepmaster Jep, I tried the optimizations, but it's about the same result. Seems like the JSON parsing is quite slow. I'll try `serde` instead of the `rustc_serialize` version. – xijo May 26 '15 at 18:35
0

That happens because definitions of "string" in Ruby and Rust don't match.

Ruby FFI expects it to be a char* from C, that is, a pointer to array of characters (see here, create_object function). So Ruby attempts to dereference it as a pointer to get character data and fails, because it's not really a pointer.

Rust has its own String class that is not just char* from C. Exporting strings from Rust in form of pointers is tricky and generic enough to deserve a separate question, and this answer should help you out.

Community
  • 1
  • 1
D-side
  • 9,150
  • 3
  • 28
  • 44