Derive Macro magnus::TypedData

source ·
#[derive(TypedData)]
{
    // Attributes available to this derive:
    #[magnus]
}
Expand description

Derives TypedData, allowing the type to be passed to Ruby automatically wrapped as a Ruby object.

For simple cases, see wrap.

§Attributes

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

  • 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] and DataTypeFunctions::free implementations do not call Ruby.
  • mark - Enable Ruby calling the DataTypeFunctions::mark function.
  • size - Enable Ruby calling the DataTypeFunctions::size function.
  • compact - Enable Ruby calling the DataTypeFunctions::compact function.
  • wb_protected - Enable the wb_protected flag.
  • frozen_shareable - Enable the frozen_shareable flag.
  • 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.

§Field Attributes

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

  • opaque_attr_reader - For a Ruby value wrapped in Opaque, creates a accessor method that returns the unwrapped Ruby value.

§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

use magnus::{DataTypeFunctions, TypedData};

#[derive(DataTypeFunctions, TypedData)]
#[magnus(class = "RbPoint", size, free_immediately)]
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 magnus::{class, define_class};

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

#[magnus::init]
fn init() -> Result<(), magnus::Error> {
    let shape = define_class("Shape", class::object())?;
    define_class("Circle", shape)?;
    define_class("Rectangle", shape)?;
    Ok(())
}

Defining a custom DataType function:

use std::mem::size_of_val;

use magnus::{DataTypeFunctions, TypedData};

#[derive(TypedData)]
#[magnus(class = "Name", size, free_immediately)]
struct Name {
    first: String,
    last: String,
}

impl DataTypeFunctions for Name {
    fn size(&self) -> usize {
        size_of_val(&self.first) + size_of_val(&self.last)
    }
}

A struct containing Ruby values.

use magnus::{
    class, define_class, function, gc, method, prelude::*, typed_data::Obj, value::Opaque,
    DataTypeFunctions, TypedData,
};

#[derive(TypedData)]
#[magnus(class = "Line", free_immediately, mark)]
struct Line {
    #[magnus(opaque_attr_reader)]
    start: Opaque<Obj<Point>>,
    #[magnus(opaque_attr_reader)]
    end: Opaque<Obj<Point>>,
}

impl Line {
    fn new(start: Obj<Point>, end: Obj<Point>) -> Self {
        Self {
            start: start.into(),
            end: end.into(),
        }
    }

    fn length(&self) -> f64 {
        let start = self.start();
        let end = self.end();

        (((end.x - start.x).pow(2) + (end.y - start.y).pow(2)) as f64).sqrt()
    }
}

impl DataTypeFunctions for Line {
    fn mark(&self, marker: &gc::Marker) {
        marker.mark(self.start);
        marker.mark(self.end);
    }
}

#[magnus::init]
fn init() -> Result<(), magnus::Error> {
    let line = define_class("Line", class::object())?;
    line.define_singleton_method("new", function!(Line::new, 2))?;
    line.define_method("length", method!(Line::length, 0))?;
    Ok(())
}