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)*) => { ... }; }
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 structcure 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,
ⓘThis example is not testedserver1.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, itself, 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 = itself.get_data(&*SERVER_WRAPPER).host(); RString::new(host) } fn ruby_server_port() -> Fixnum { let port = itself.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(|itself| { itself.def_self("new", ruby_server_new); itself.def("host", ruby_server_host); itself.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, itself, fn new() -> AnyObject { let vec = VectorOfObjects::new(); Class::from_existing("RustyArray").wrap_data(vec, &*VECTOR_OF_OBJECTS_WRAPPER) } fn push(object: AnyObject) -> NilClass { itself.get_data_mut(&*VECTOR_OF_OBJECTS_WRAPPER).push(object.unwrap()); NilClass::new() } fn length() -> Fixnum { let length = itself.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(|itself| { itself.def_self("new", new); itself.def("push", push); itself.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