Macro rutie::wrappable_struct [−][src]
macro_rules! wrappable_struct {
(@ mark_function_pointer) => { ... };
(@ mark_function_pointer, mark($object : ident) $body : block) => { ... };
(@ mark_function_definition $struct_name : ty) => { ... };
(@ mark_function_definition $struct_name : ty, mark($object : ident) $body :
expr) => { ... };
($struct_name : ty, $wrapper : ident, $static_name : ident $($tail : tt) *) => { ... };
}
Expand description
Makes a Rust struct wrappable for Ruby objects.
Note: Currently to be able to use wrappable_struct!
macro, you should include
lazy_static
crate to the crate you are working on.
Cargo.toml
lazy_static = "0.2.1" # the version is not a strict requirement
Crate root lib.rs
or main.rs
#[macro_use]
extern crate lazy_static;
Arguments
-
$struct_name
is name of the actual Rust struct. This structure has to be public (pub
). -
$wrapper
is a name for the structure which will be created to wrap the$struct_name
.The wrapper will be created automatically by the macro.
-
$static_name
is a name for a static variable which will contain the wrapper.The static variable will be created automatically by the macro.
This variable has to be passed to
wrap_data()
andget_data()
functions (see examples).Also, these variables describe the structure in general, but not some specific object. So you should pass the same static variable when wrapping/getting data of the same type for different ruby objects.
For example,
ⓘserver1.get_data(&*SERVER_WRAPPER); server2.get_data(&*SERVER_WRAPPER); // <-- the same `SERVER_WRAPPER`
-
(optional)
mark(data) { ... }
is a block which will be called during the “mark” phase of garbage collection.This block must be used if the struct contains any Ruby objects. The objects should be marked with
GC::mark()
to prevent their garbage collection.data
argument will be yielded as a mutable reference to the wrapped struct (&mut $struct_name
).Notes from the official MRI documentation:
-
It is not recommended to store Ruby objects in the structs. Try to avoid that if possible.
-
It is not allowed to allocate new Ruby objects in the
mark
function.
-
The result of wrappable_struct!
is:
wrappable_struct!(Server, ServerWrapper, SERVER_WRAPPER);
// produces
struct ServerWrapper {
// ...
}
pub static ref SERVER_WRAPPER: ServerWrapper<Server> = // ...
Class
The class which will be used for wrapping data is Object
and not Data
(See Ruby issue #3072).
let data_class = Class::from_existing("Object");
Class::new("TheNewClass", Some(&data_class));
Examples
Wrap Server
structs to RubyServer
objects
#[macro_use] extern crate rutie;
#[macro_use] extern crate lazy_static;
use rutie::{AnyObject, Class, Fixnum, Object, RString, VM};
// The structure which we want to wrap
pub struct Server {
host: String,
port: u16,
}
impl Server {
fn new(host: String, port: u16) -> Self {
Server {
host: host,
port: port,
}
}
fn host(&self) -> &str {
&self.host
}
fn port(&self) -> u16 {
self.port
}
}
wrappable_struct!(Server, ServerWrapper, SERVER_WRAPPER);
class!(RubyServer);
methods!(
RubyServer,
rtself,
fn ruby_server_new(host: RString, port: Fixnum) -> AnyObject {
let server = Server::new(host.unwrap().to_string(),
port.unwrap().to_i64() as u16);
Class::from_existing("RubyServer").wrap_data(server, &*SERVER_WRAPPER)
}
fn ruby_server_host() -> RString {
let host = rtself.get_data(&*SERVER_WRAPPER).host();
RString::new_utf8(host)
}
fn ruby_server_port() -> Fixnum {
let port = rtself.get_data(&*SERVER_WRAPPER).port();
Fixnum::new(port as i64)
}
);
fn main() {
let data_class = Class::from_existing("Object");
Class::new("RubyServer", Some(&data_class)).define(|klass| {
klass.def_self("new", ruby_server_new);
klass.def("host", ruby_server_host);
klass.def("port", ruby_server_port);
});
}
To use the RubyServer
class in Ruby:
server = RubyServer.new("127.0.0.1", 3000)
server.host == "127.0.0.1"
server.port == 3000
RustyArray
Custom array implementation using a vector which contains AnyObject
s.
#[macro_use] extern crate rutie;
#[macro_use] extern crate lazy_static;
use std::ops::{Deref, DerefMut};
use rutie::{AnyObject, Class, Fixnum, GC, NilClass, Object, VM};
pub struct VectorOfObjects {
inner: Vec<AnyObject>,
}
impl VectorOfObjects {
fn new() -> Self {
VectorOfObjects {
inner: Vec::new(),
}
}
}
impl Deref for VectorOfObjects {
type Target = Vec<AnyObject>;
fn deref(&self) -> &Vec<AnyObject> {
&self.inner
}
}
impl DerefMut for VectorOfObjects {
fn deref_mut(&mut self) -> &mut Vec<AnyObject> {
&mut self.inner
}
}
wrappable_struct! {
VectorOfObjects,
VectorOfObjectsWrapper,
VECTOR_OF_OBJECTS_WRAPPER,
// Mark each `AnyObject` element of the `inner` vector to prevent garbage collection.
// `data` is a mutable reference to the wrapped data (`&mut VectorOfObjects`).
mark(data) {
for object in &data.inner {
GC::mark(object);
}
}
}
class!(RustyArray);
methods! {
RustyArray,
rtself,
fn new() -> AnyObject {
let vec = VectorOfObjects::new();
Class::from_existing("RustyArray").wrap_data(vec, &*VECTOR_OF_OBJECTS_WRAPPER)
}
fn push(object: AnyObject) -> NilClass {
rtself.get_data_mut(&*VECTOR_OF_OBJECTS_WRAPPER).push(object.unwrap());
NilClass::new()
}
fn length() -> Fixnum {
let length = rtself.get_data(&*VECTOR_OF_OBJECTS_WRAPPER).len() as i64;
Fixnum::new(length)
}
}
fn main() {
let data_class = Class::from_existing("Object");
Class::new("RustyArray", Some(&data_class)).define(|klass| {
klass.def_self("new", new);
klass.def("push", push);
klass.def("length", length);
});
}
To use the RustyArray
class in Ruby:
array = RustyArray.new
array.push(1)
array.push("string")
array.push(:symbol)
array.length == 3