Reputation: 597
I am thinking about writing a Ruby gem with Rust. Lets assume I want to create some structs in Rust which are returned to the Ruby code similar to the example here. While getting the Point struct to my Ruby code, I would like to call its attributes directly. Currently I would have to do something like that:
point.rb:
require "fiddle"
require "fiddle/import"
module RustPoint
extend Fiddle::Importer
dlload "./libmain.dylib"
extern "Point* make_point(int, int)"
extern "double get_distance(Point*, Point*)"
extern "int y(Point*)"
extern "int x(Point*)"
end
main.rs:
use std::num::pow;
pub struct Point { x: int, y: int }
#[no_mangle]
pub extern "C" fn make_point(x: int, y: int) -> Box<Point> {
box Point { x: x, y: y }
}
#[no_mangle]
pub extern "C" fn x(p: &Point) -> int {
p.x
}
#[no_mangle]
pub extern "C" fn y(p: &Point) -> int {
p.y
}
and use this in Ruby:
point = RustPoint::make_point(0, 42)
# To get x:
x = RustPoint::x(point)
to get an x value. I would prefer something like:
point = RustPoint::make_point(0, 42)
# To get x:
x = point.x
Does anyone know a library or a way to get this implemented easier. I think it would be much nicer if i wouldn't see a different regarding the point object from ruby side. i should not make a difference weather this is a C extension, a Ruby object or written in Rust.
Edit: I want the Rust code to behave like a native extension. So the returned struct should be callable from Ruby side similar to a C struct using ruby objects as values. Of course a library would be nessessary to handle the ruby objects in rust code.
Upvotes: 1
Views: 267
Reputation: 15002
Rust provides a native C interface for struct
as well. If you define your struct like this:
#[repr(C)]
pub struct Point {
pub x: i32,
pub y: i32
}
It will behave like the C struct
struct Point
{
int32_t x;
int32_t y;
}
You may then use it in Ruby like any other C struct.
I recommend using the fixed size int types rather than plain int
, because you have no real guaranty Rust's int
are the same size as C's int
. If you really need to use it, you should probably use libc::c_int
.
Upvotes: 0
Reputation: 54684
You could wrap the whole thing in a custom delegator:
class RustDelegator
attr_accessor :__delegate_class__, :__delegate__
def method_missing(method_name, *arguments, &block)
__delegate_class__.public_send(method_name, *__rust_arguments__(arguments), &block)
end
def respond_to_missing(name, include_private = false)
__delegate_class__.respond_to?(name, include_private)
end
private
def __rust_arguments__(arguments)
arguments.unshift(__delegate__)
end
end
class Point < RustDelegator
def initialize(x, y)
self.__delegate_class__ = RustPoint
self.__delegate__ = RustPoint::make_point(0, 42)
end
end
p = Point.new(0, 42)
#=> #<Point:0x007fb4a4b5b9d0 @__delegate__=[0, 42], @__delegate_class__=RustPoint>
p.x
#=> 0
p.y
#=> 42
Upvotes: 1