Trait magnus::class::Class

source ·
pub trait Class: Module {
    type Instance;

    // Required methods
    fn new(superclass: Self) -> Result<Self, Error>;
    fn new_instance<T>(self, args: T) -> Result<Self::Instance, Error>
       where T: ArgList;
    fn obj_alloc(self) -> Result<Self::Instance, Error>;

    // Provided methods
    fn superclass(self) -> Result<RClass, Error> { ... }
    unsafe fn name(&self) -> Cow<'_, str> { ... }
    fn as_r_class(self) -> RClass { ... }
    fn define_alloc_func<T>(self)
       where T: Default + TypedData { ... }
    fn undef_default_alloc_func(self) { ... }
}
Expand description

Functions available on all types representing a Ruby class.

Required Associated Types§

source

type Instance

The type representing an instance of the class Self.

Required Methods§

source

fn new(superclass: Self) -> Result<Self, Error>

Create a new anonymous class.

§Examples
use magnus::{class, prelude::*, RClass};

let class = RClass::new(class::object()).unwrap();
assert!(class.is_kind_of(class::class()));
use magnus::{exception, prelude::*, ExceptionClass};

assert!(ExceptionClass::new(exception::standard_error()).is_ok());
source

fn new_instance<T>(self, args: T) -> Result<Self::Instance, Error>
where T: ArgList,

Create a new object, an instance of self, passing the arguments args to the initialiser.

§Examples
use magnus::{class, prelude::*};

let s = class::string().new_instance(()).unwrap();
assert!(s.is_kind_of(class::string()));
assert_eq!(s.to_string(), "");
use magnus::{eval, kwargs, prelude::*, RClass};

let cls: RClass = eval!(
    r#"
    class Foo
      def initialize(bar, baz:)
        @bar = bar
        @baz = baz
      end

      attr_reader(:bar, :baz)
    end

    Object.const_get(:Foo)
"#
)
.unwrap();
let instance = cls.new_instance((1, kwargs!("baz" => 2))).unwrap();
assert!(instance.is_kind_of(cls));
let bar: i32 = instance.funcall("bar", ()).unwrap();
assert_eq!(bar, 1);
let baz: i32 = instance.funcall("baz", ()).unwrap();
assert_eq!(baz, 2);
use magnus::{exception, prelude::*};

let s = exception::standard_error()
    .new_instance(("bang!",))
    .unwrap();
assert!(s.is_kind_of(exception::standard_error()));
use magnus::{eval, ExceptionClass, kwargs, prelude::*};

let exc: ExceptionClass = eval!(
    r#"
    class MyError < StandardError
      def initialize(message:)
        super(message)
      end
    end

    Object.const_get(:MyError)
"#
).unwrap();
let s = exc.new_instance((kwargs!("message" => "bang!"),)).unwrap();
assert!(s.is_kind_of(exc));
let message: String = s.funcall("message", ()).unwrap();
assert_eq!(message, "bang!");
source

fn obj_alloc(self) -> Result<Self::Instance, Error>

Create a new object, an instance of self, without calling the class’s initialize method.

§Examples
use magnus::{class, prelude::*};

let s = class::string().obj_alloc().unwrap();
assert!(s.is_kind_of(class::string()));
use magnus::{exception, prelude::*};

let s = exception::standard_error().obj_alloc().unwrap();
assert!(s.is_kind_of(exception::standard_error()));

Provided Methods§

source

fn superclass(self) -> Result<RClass, Error>

Returns the parent class of self.

Returns Err if self can not have a parent class.

§Examples
use magnus::{class, prelude::*};

let klass = class::hash().superclass().unwrap();
assert!(klass.equal(class::object()).unwrap());
use magnus::{class, exception, prelude::*};

let klass = exception::exception().superclass().unwrap();
assert!(klass.equal(class::object()).unwrap());
source

unsafe fn name(&self) -> Cow<'_, str>

Return the name of self.

§Safety

Ruby may modify or free the memory backing the returned str, the caller must ensure this does not happen.

This can be used safely by immediately calling into_owned on the return value.

§Examples
use magnus::{class, prelude::*};

let value = class::hash();
// safe as we neve give Ruby a chance to free the string.
let s = unsafe { value.name() }.into_owned();
assert_eq!(s, "Hash");
use magnus::{exception, prelude::*};

let value = exception::standard_error();
// safe as we neve give Ruby a chance to free the string.
let s = unsafe { value.name() }.into_owned();
assert_eq!(s, "StandardError");
source

fn as_r_class(self) -> RClass

Return self as an RClass.

source

fn define_alloc_func<T>(self)
where T: Default + TypedData,

Define an allocator function for self using T’s Default implementation.

In Ruby creating a new object has two steps, first the object is allocated, and then it is initialised. Allocating the object is handled by the new class method, which then also calls initialize on the newly allocated object.

This does not map well to Rust, where data is allocated and initialised in a single step. For this reason most examples in this documentation show defining the new class method directly, opting out of the two step allocate and then initialise process. However, this means the class can’t be subclassed in Ruby.

Defining an allocator function allows a class be subclassed with the normal Ruby behaviour of calling the initialize method.

Be aware when creating an instance of once of a class with an allocator function from Rust it must be done with Class::new_instance to call the allocator and then the initialize method.

§Panics

Panics if self and <T as TypedData>::class() are not the same class.

§Examples
use std::cell::RefCell;

use magnus::{
    class, define_class, embed, eval, function, method, prelude::*, wrap, Error, RClass, Value,
};

#[derive(Default)]
struct Point {
    x: isize,
    y: isize,
}

#[derive(Default)]
#[wrap(class = "Point")]
struct MutPoint(RefCell<Point>);

impl MutPoint {
    fn initialize(&self, x: isize, y: isize) {
        let mut this = self.0.borrow_mut();
        this.x = x;
        this.y = y;
    }

    // bypasses initialize
    fn create(x: isize, y: isize) -> MutPoint {
        MutPoint(RefCell::new(Point { x, y }))
    }

    // calls initialize
    fn call_new(class: RClass, x: isize, y: isize) -> Result<Value, Error> {
        class.new_instance((x, y))
    }

    fn distance(&self, other: &MutPoint) -> f64 {
        let a = self.0.borrow();
        let b = other.0.borrow();
        (((b.x - a.x).pow(2) + (b.y - a.y).pow(2)) as f64).sqrt()
    }
}

let class = define_class("Point", class::object()).unwrap();
class.define_alloc_func::<MutPoint>();
class
    .define_singleton_method("create", function!(MutPoint::create, 2))
    .unwrap();
class
    .define_singleton_method("call_new", method!(MutPoint::call_new, 2))
    .unwrap();
class
    .define_method("initialize", method!(MutPoint::initialize, 2))
    .unwrap();
class
    .define_method("distance", method!(MutPoint::distance, 1))
    .unwrap();

let d: f64 = eval(
    "class OffsetPoint < Point
       def initialize(offset, x, y)
         super(x + offset, y + offset)
       end
     end
     a = Point.new(1, 1)
     b = OffsetPoint.new(2, 3, 3)
     a.distance(b).round(2)",
)
.unwrap();

assert_eq!(d, 5.66);
source

fn undef_default_alloc_func(self)

Remove the allocator function of a class if it is Ruby’s default allocator function.

Useful for RTypedData, where instances should not be allocated by the default allocate function. #[derive(TypedData)] and #[wrap] take care of undefining the allocator function, you do not need to use undef_default_alloc_func if you’re using one of those.

§Examples
use magnus::{class, Class};
let class = magnus::define_class("Point", class::object()).unwrap();

class.undef_default_alloc_func();

let instance = class.new_instance(());
assert_eq!(
    "allocator undefined for Point",
    instance.err().unwrap().to_string()
);

Object Safety§

This trait is not object safe.

Implementors§