Macro ruru::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 wrapper static variable when wrapping/getting data of the same kind 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 must inherit from
Data
class instead of Object
.
let data_class = Class::from_existing("Data"); Class::new("TheNewClass", Some(&data_class));
Examples
Wrap Server
structs to RubyServer
objects
#[macro_use] extern crate ruru; #[macro_use] extern crate lazy_static; use ruru::{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("Data"); 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 ruru; #[macro_use] extern crate lazy_static; use std::ops::{Deref, DerefMut}; use ruru::{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(&*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("Data"); 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