Attribute Macro magnus::wrap

source ·
#[wrap]
Expand description

Allow a Rust type to be passed to Ruby, automatically wrapped as a Ruby object.

For more control over the wrapped object, see TypedData.

§Attributes

  • class = "..." - required, sets the Ruby class to wrap the Rust type. Supports module paths, e.g. Foo::Bar::Baz.
  • name = "..." - debug name for the type, must be unique. Defaults to the class name.
  • free_immediately - Drop the Rust type as soon as the Ruby object has been garbage collected. This is only safe to set if the type’s [Drop] implmentation does not call Ruby.
  • size - Report the [std::mem::size_of_val] of the type to Ruby, used to aid in deciding when to run the garbage collector.
  • unsafe_generics - The derived implementation of TypedData is not guaranteed to be correct for types with generics. If you are sure it is for your type this attribute can be used to override the compile time error usually generated for types with generics.

§Variant Attributes

The #[magnus(...)] attribute can be set on enum variants with the following values:

  • class = "..." - sets the Ruby class to wrap the variant. Supports module paths, e.g. Foo::Bar::Baz.

§Examples

#[magnus::wrap(class = "RbPoint", free_immediately, size)]
struct Point {
    x: isize,
    y: isize,
}

// the `Point` struct is automatically wrapped in a Ruby `RbPoint` object
// when returned to Ruby.
fn point(x: isize, y: isize) -> Point {
    Point { x, y }
}

// Ruby `RbPoint` objects are automatically unwrapped to references to the
// `Point` structs they are wrapping when this function is called from Ruby.
fn distance(a: &Point, b: &Point) -> f64 {
    (((b.x - a.x).pow(2) + (b.y - a.y).pow(2)) as f64).sqrt()
}

#[magnus::init]
fn init() {
    magnus::define_global_function("point", magnus::function!(point, 2));
    magnus::define_global_function("distance", magnus::function!(distance, 2));
}

With subclasses for enum variants:

use std::f64::consts::PI;

use magnus::{class, define_class, function, method, prelude::*};

#[magnus::wrap(class = "Shape")]
enum Shape {
    #[magnus(class = "Circle")]
    Circle { r: f64 },
    #[magnus(class = "Rectangle")]
    Rectangle { x: f64, y: f64 },
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { r } => PI * r * r,
            Shape::Rectangle { x, y } => x * y,
        }
    }
}

#[magnus::init]
fn init() -> Result<(), magnus::Error> {
    let shape = define_class("Shape", class::object())?;
    shape.define_method("area", method!(Shape::area, 0))?;

    let circle = define_class("Circle", shape)?;
    circle.define_singleton_method("new", function!(|r| Shape::Circle { r }, 1))?;

    let rectangle = define_class("Rectangle", shape)?;
    rectangle.define_singleton_method("new", function!(|x, y| Shape::Rectangle { x, y }, 2))?;

    Ok(())
}