9

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
mpiccolo
  • 662
  • 5
  • 14
  • 7
    Quick comment: there are two immediate problems with this. Firstly, `CStr` **is not a FFI-safe type** and should not be used like this. You should be using `*const libc::c_char`. Secondly, the answer depends entirely on who, exactly, owns the resulting string. Does Ruby manage it? If so, how? Which heap does it need to be allocated on? Does Rust still own it? If so, how do you provide a cleanup function? All I can recommend is to find out how to do this in C, then just do it that way in Rust. – DK. Jun 24 '15 at 08:56
  • 2
    Also look at http://stackoverflow.com/q/30510764/155423, as it covers all the important parts. Adapting it to Ruby is easy at that point. – Shepmaster Jun 24 '15 at 12:20
  • 1
    http://clalance.blogspot.ca/2011/01/writing-ruby-extensions-in-c-part-8.html will be helpful, as will be https://github.com/ruby/ruby/blob/trunk/doc/extension.rdoc – Steve Klabnik Jun 24 '15 at 16:50
  • Update on mpiccolo solution: According to this discussion https://github.com/rust-lang/rust/issues/27769 `into_ptr` is now called `into_raw` Tested and working with `into_raw` on my side – Sébastien Faure May 11 '23 at 08:32

1 Answers1

6

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'
Community
  • 1
  • 1
mpiccolo
  • 662
  • 5
  • 14