Search code examples
rubystringrustffi

Ruby string to rust and back again


I am trying to pass a string in Ruby to a rust executable, manipulate it and pass the manipulated string back.

So far I can pass the string in and return it but I am at a loss as to how to convert it into a rust string, manipulate it and then pass it back to ruby. Here is what I have so far:

// lib.rs
use std::ffi::CStr;

#[no_mangle]
pub extern fn return_string(test_str: &CStr) -> &CStr {
    // working funciton
    test_str
}

#[no_mangle]
pub extern fn manipulate_and_return_string(mystr: &CStr) -> &CStr {
    // mystr type == &std::ffi::c_str::CStr
    // println!("{:?}", mystr); => std::ffi::c_str::CStr` cannot be formatted using `:?`
    let cstr = mystr.to_bytes_with_nul();
    // println!("{:?}", mystr); => []
    // cstr type == &[u8]
    let ptr = cstr.as_ptr();
    // ptr type == *const u8
    // println!("{:?}", mystr); => 0x7fd898edb520
    let str_slice: &str = std::str::from_utf8(cstr).unwrap();
    // str type == &str
    // println!("{:?}", mystr); => ""
    let str_buf: String = str_slice.to_owned();
    // str_bug == collections::string::String
    // println!("{:?}", mystr); => ""
}
# rust.rb
require 'ffi'

module Rust
  extend FFI::Library
  ffi_lib './bin/libembed.dylib'

  attach_function :return_string, [:string], :string
  attach_function :manipulate_and_return_string, [:string], :string
end

Solution

  • Thanks to some guidance from Steve Klabnik, shepmaster and DK I figured out how to write a external string concat function in Rust and use it in Ruby.

    // lib.rs
    #![feature(libc)]
    #![feature(cstr_to_str)]
    #![feature(cstr_memory)]
    extern crate libc;
    use std::ffi::{CStr,CString};
    
    #[no_mangle]
    pub extern fn concat(s1: *const libc::c_char, s2: *const libc::c_char) -> *const libc::c_char {
        let s1_cstr = unsafe { CStr::from_ptr(s1) };  // &std::ffi::c_str::CStr
        let s2_cstr = unsafe { CStr::from_ptr(s2) };  // &std::ffi::c_str::CStr
        let s1_and_str = s1_cstr.to_str().unwrap();  // &str
        let s2_and_str = s2_cstr.to_str().unwrap();  // &str
    
        let mut s1_string = s1_and_str.to_string();  // collections::string::String
    
        s1_string.push_str(s2_and_str);
        // s1_string + s2_and_str); // same thing
    
        let concated_string = CString::new(s1_string).unwrap();  // std::ffi::c_str::CString
    
        concated_string.into_ptr() // const i8
    }
    
    # rust.rb
    require 'ffi'
    
    module Rust
      extend FFI::Library
      ffi_lib './bin/libembed.dylib'
    
      attach_function :concat, [:string, :string], :string
    end
    
    #calling the function in Ruby
    
    Rust.concat('This is a ', 'cohesive sentence') # => 'This is a cohesive sentence'