3

I am trying to return a struct that can be converted into a Ruby array from an external rust function but when I try to call the structs #to_a method I get a segfault.

use libc::size_t;
#[repr(C)]
pub struct Array {
    len: libc::size_t,
    data: *const libc::c_void,
}

impl Array {
    fn from_vec<T>(mut vec: Vec<T>) -> Array {

        vec.shrink_to_fit();

        let array = Array { data: vec.as_ptr() as *const libc::c_void, len: vec.len() as libc::size_t };

        mem::forget(vec);

        array
    }
}

#[no_mangle]
pub extern fn get_links(url: *const libc::c_char) -> Array {

  // Get links

  let mut urls: Vec<String> = vec![];

  // push strings into urls vec

  // urls => collections::vec::Vec<collections::string::String>

  Array::from_vec(urls)
}
require 'ffi'

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

  class NodesArray < FFI::Struct
      layout :len,    :size_t, # dynamic array layout
             :data,   :pointer #

      def to_a
          self[:data].get_array_of_string(0, self[:len]).compact
      end
  end

  attach_function :get_links, [:string], NodesArray.by_value
end

When I try to use this function in ruby it will return the Fii::NodesArray. I can also get the len and data off of the struct. It is only when I call the #to_a that segfaults.

3
  • I'm not familiar with Ruby FFI but... are len and data supposed to be reversed? It's data, len in Rust, but len, data in Ruby. Commented Jul 2, 2015 at 17:10
  • Hmm. Good point. However I do believe I had them in the correct layout at some point and was still segfaulting. I will try switching that and update the question. Commented Jul 2, 2015 at 17:17
  • 1
    You now have two separate definitions of the Array struct in your rust source... please post the code that you are using Commented Jul 2, 2015 at 18:01

2 Answers 2

2

The issue, pointed out by Adrian, was that I was pushing strings into the Vec. FFI needs *const libc::c_char, which can be converted from a String.

let mut urls: Vec<*const libc::c_char> = vec![];

urls.push(CString::new(string_var.value.to_string()).unwrap().into_raw());
Sign up to request clarification or add additional context in comments.

Comments

1

It seems like FFI::Pointer#get_array_of_string is bugged (or it just doesn't do what I think it does). Your code works for me if I change this line:

self[:data].get_array_of_string(0, self[:len]).compact

to this:

Array.new(self[:len]) {|i| self[:data].read_pointer[i * FFI::Pointer::SIZE].read_string }

5 Comments

As a side note, you may want to consider using std::ffi::CString instead of String
Awesome. Thank you Adrian. And yes CString is what I should have been using.
One issue though is that I am getting an array of strings only pointing to the first sting in the array. So I am getting an array that looks like this: ["google.com/webhp?tab=ww", "www.google.com/webhp?tab=ww", "le.com/webhp?tab=ww", "ebhp?tab=ww", "=ww", "\t", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
@mpiccolo I can't imagine why that would be happening. I would recommend posting a new question with a complete example program, the output you are getting, and your expected output. Without that information it is hard to help you.
I posted the fix above. You pointed me in the right direction with reminding me I needed a CString instead of a String.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.