Thanks to visit codestin.com
Credit goes to docs.rs

magnus/
typed_data.rs

1//! Types and Traits for wrapping Rust types as Ruby objects.
2//!
3//! This, along with [`RTypedData`], provides a Rust API to the
4//! `rb_data_typed_object_wrap` function from Ruby's C API.
5
6use std::{
7    collections::hash_map::DefaultHasher,
8    ffi::{c_void, CStr},
9    fmt,
10    hash::Hasher,
11    marker::PhantomData,
12    mem::size_of_val,
13    ops::Deref,
14    panic::catch_unwind,
15    ptr,
16};
17
18#[cfg(ruby_gte_3_0)]
19use rb_sys::rbimpl_typeddata_flags::{self, RUBY_TYPED_FREE_IMMEDIATELY, RUBY_TYPED_WB_PROTECTED};
20use rb_sys::{
21    self, rb_data_type_struct__bindgen_ty_1, rb_data_type_t, rb_gc_writebarrier,
22    rb_gc_writebarrier_unprotect, rb_obj_reveal, rb_singleton_class_attached,
23    rb_singleton_class_clone, size_t, RTYPEDDATA_GET_DATA, VALUE,
24};
25
26#[cfg(ruby_lt_3_0)]
27const RUBY_TYPED_FREE_IMMEDIATELY: u32 = 1;
28
29#[cfg(ruby_lt_3_0)]
30const RUBY_TYPED_WB_PROTECTED: u32 = rb_sys::ruby_fl_type::RUBY_FL_WB_PROTECTED as u32;
31
32use crate::{
33    class::RClass,
34    error::{bug_from_panic, Error},
35    gc::{self, Mark},
36    into_value::IntoValue,
37    object::Object,
38    r_typed_data::RTypedData,
39    scan_args::{get_kwargs, scan_args},
40    try_convert::TryConvert,
41    value::{
42        private::{self, ReprValue as _},
43        ReprValue, Value,
44    },
45    Ruby,
46};
47
48/// A C struct containing metadata on a Rust type, for use with the
49/// `rb_data_typed_object_wrap` API.
50#[repr(transparent)]
51pub struct DataType(rb_data_type_t);
52
53impl DataType {
54    /// Create a new `DataTypeBuilder`.
55    ///
56    /// `name` should be unique per wrapped type. It does not need to be a
57    /// valid Ruby identifier.
58    ///
59    /// See [`data_type_builder`](macro@crate::data_type_builder) to create a
60    /// `DataTypeBuilder` with a `'static CStr` `name` from a string literal.
61    pub const fn builder<T>(name: &'static CStr) -> DataTypeBuilder<T>
62    where
63        T: DataTypeFunctions,
64    {
65        DataTypeBuilder::new(name)
66    }
67
68    #[inline]
69    pub(crate) fn as_rb_data_type(&self) -> &rb_data_type_t {
70        &self.0
71    }
72}
73
74unsafe impl Send for DataType {}
75unsafe impl Sync for DataType {}
76
77/// A helper trait used to define functions associated with a [`DataType`].
78pub trait DataTypeFunctions
79where
80    Self: Send + Sized,
81{
82    /// Called when the Ruby wrapper object is garbage collected.
83    ///
84    /// This can be implemented to perform Ruby-specific clean up when your
85    /// type is no longer referenced from Ruby, but it is likely easier to do
86    /// this in a [`Drop`] implementation for your type.
87    ///
88    /// This function will always be called by Ruby on GC, it can not be opted
89    /// out of.
90    ///
91    /// The default implementation simply drops `self`.
92    ///
93    /// If this function (or the [`Drop`] implementation for your type) call
94    /// Ruby APIs you should not enable the `free_immediately` flag with the
95    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
96    /// macro or [`DataTypeBuilder::free_immediately`].
97    ///
98    /// This function **must not** panic. The process will abort if this
99    /// function panics.
100    fn free(self: Box<Self>) {}
101
102    /// Called when Ruby marks this object as part of garbage collection.
103    ///
104    /// If your type contains any Ruby values you must mark each of those
105    /// values in this function to avoid them being garbage collected.
106    ///
107    /// This function is only called when the `mark` flag is set with the
108    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
109    /// macro or [`DataTypeBuilder::mark`].
110    ///
111    /// The default implementation does nothing.
112    ///
113    /// This function **must not** panic. The process will abort if this
114    /// function panics.
115    fn mark(&self, #[allow(unused_variables)] marker: &gc::Marker) {}
116
117    /// Called by Ruby to establish the memory size of this data, to optimise
118    /// when garbage collection happens.
119    ///
120    /// This function is only called when the `size` flag is set with the
121    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
122    /// macro or [`DataTypeBuilder::mark`].
123    ///
124    /// The default implementation delegates to [`std::mem::size_of_val`].
125    ///
126    /// This function **must not** panic. The process will abort if this
127    /// function panics.
128    fn size(&self) -> usize {
129        size_of_val(self)
130    }
131
132    /// Called during garbage collection.
133    ///
134    /// If your type contains any Ruby values that you have marked as moveable
135    /// in your [`mark`](Self::mark) function, you must update them in this
136    /// function using [`gc::Compactor::location`].
137    ///
138    /// Ruby considers values are moveable if marked with the
139    /// [`gc::Marker::mark_movable`] function. Other marking functions such as
140    /// [`gc::Marker::mark`] will prevent values being moved.
141    ///
142    /// As it is only safe for this function to receive a shared `&self`
143    /// reference, you must implement interior mutability to be able to update
144    /// values. This is very hard to do correctly, and it is recommended to
145    /// simply avoid using [`gc::Marker::mark_movable`] and `compact`.
146    ///
147    /// This function is only called when the `compact` flag is set with the
148    /// [`wrap`](macro@crate::wrap)/[`TypedData`](macro@crate::TypedData)
149    /// macro or [`DataTypeBuilder::mark`].
150    ///
151    /// The default implementation does nothing.
152    ///
153    /// This function **must not** panic. The process will abort if this
154    /// function panics.
155    fn compact(&self, #[allow(unused_variables)] compactor: &gc::Compactor) {}
156
157    /// Extern wrapper for `free`. Don't define or call.
158    ///
159    /// # Safety
160    ///
161    /// `ptr` must be a valid pointer to a `Box<Self>`, and must not be aliased
162    /// This function will free the memory pointed to by `ptr`.
163    ///
164    /// This function must not panic.
165    #[doc(hidden)]
166    unsafe extern "C" fn extern_free(ptr: *mut c_void) {
167        if let Err(e) = catch_unwind(|| Self::free(Box::from_raw(ptr as *mut _))) {
168            bug_from_panic(e, "panic in DataTypeFunctions::free")
169        }
170    }
171
172    /// Extern wrapper for `mark`. Don't define or call.
173    ///
174    /// # Safety
175    ///
176    /// `ptr` must be a valid pointer to a `Self`, and must not be aliased.
177    ///
178    /// This function must not panic.
179    #[doc(hidden)]
180    unsafe extern "C" fn extern_mark(ptr: *mut c_void) {
181        let marker = gc::Marker::new();
182        if let Err(e) = catch_unwind(|| Self::mark(&*(ptr as *mut Self), &marker)) {
183            bug_from_panic(e, "panic in DataTypeFunctions::mark")
184        }
185    }
186
187    /// Extern wrapper for `size`. Don't define or call.
188    ///
189    /// # Safety
190    ///
191    /// `ptr` must be a valid pointer to a `Self`.
192    ///
193    /// This function must not panic.
194    #[doc(hidden)]
195    unsafe extern "C" fn extern_size(ptr: *const c_void) -> size_t {
196        match catch_unwind(|| Self::size(&*(ptr as *const Self)) as size_t) {
197            Ok(v) => v,
198            Err(e) => bug_from_panic(e, "panic in DataTypeFunctions::size"),
199        }
200    }
201
202    /// Extern wrapper for `compact`. Don't define or call.
203    ///
204    /// # Safety
205    ///
206    /// `ptr` must be a valid pointer to a `Self`, and must not be aliased.
207    ///
208    /// This function must not panic.
209    #[doc(hidden)]
210    unsafe extern "C" fn extern_compact(ptr: *mut c_void) {
211        let compactor = gc::Compactor::new();
212        if let Err(e) = catch_unwind(|| Self::compact(&*(ptr as *mut Self), &compactor)) {
213            bug_from_panic(e, "panic in DataTypeFunctions::compact")
214        }
215    }
216}
217
218/// A builder for [`DataType`].
219pub struct DataTypeBuilder<T> {
220    name: &'static CStr,
221    mark: bool,
222    size: bool,
223    compact: bool,
224    free_immediately: bool,
225    wb_protected: bool,
226    frozen_shareable: bool,
227    phantom: PhantomData<T>,
228}
229
230/// Create a new [`DataTypeBuilder`].
231///
232/// `name` should be unique per wrapped type. It does not need to be a
233/// valid Ruby identifier.
234///
235/// `data_type_builder!(Example, "example")` is equivalent to
236/// `DataTypeBuilder::<Example>::new` with a `name` argument of `"example"` as
237/// a `'static CStr`.
238#[macro_export]
239macro_rules! data_type_builder {
240    ($t:ty, $name:literal) => {
241        $crate::typed_data::DataTypeBuilder::<$t>::new(unsafe {
242            std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($name, "\0").as_bytes())
243        })
244    };
245}
246
247impl<T> DataTypeBuilder<T>
248where
249    T: DataTypeFunctions,
250{
251    /// Create a new `DataTypeBuilder`.
252    ///
253    /// `name` should be unique per wrapped type. It does not need to be a
254    /// valid Ruby identifier.
255    ///
256    /// See [`data_type_builder`](macro@crate::data_type_builder) to create a
257    /// `DataTypeBuilder` with a `'static CStr` `name` from a string literal.
258    pub const fn new(name: &'static CStr) -> Self {
259        Self {
260            name,
261            mark: false,
262            size: false,
263            compact: false,
264            free_immediately: false,
265            wb_protected: false,
266            frozen_shareable: false,
267            phantom: PhantomData,
268        }
269    }
270
271    /// Enable using the the `mark` function from `<T as DataTypeFunctions>`.
272    pub const fn mark(mut self) -> Self {
273        self.mark = true;
274        self
275    }
276
277    /// Enable using the the `size` function from `<T as DataTypeFunctions>`.
278    pub const fn size(mut self) -> Self {
279        self.size = true;
280        self
281    }
282
283    /// Enable using the the `compact` function from `<T as DataTypeFunctions>`.
284    pub const fn compact(mut self) -> Self {
285        self.compact = true;
286        self
287    }
288
289    /// Enable the 'free_immediately' flag.
290    ///
291    /// This is safe to do as long as the `<T as DataTypeFunctions>::free`
292    /// function or `T`'s drop function don't call Ruby in any way.
293    ///
294    /// If safe this should be enabled as this performs better and is more
295    /// memory efficient.
296    pub const fn free_immediately(mut self) -> Self {
297        self.free_immediately = true;
298        self
299    }
300
301    /// Enable the 'write barrier protected' flag.
302    ///
303    /// Types that contain Ruby values by default do not participate in
304    /// generational GC (they are scanned every GC). This flag asserts all
305    /// operations that write Ruby values to this type are protected with
306    /// write barriers (see [`Writebarrier::writebarrier`]) so this
307    /// type can participate in generational GC.
308    ///
309    /// The write barrier is hard to get right. Magnus recommends you do not use
310    /// this flag.
311    pub const fn wb_protected(mut self) -> Self {
312        self.wb_protected = true;
313        self
314    }
315
316    /// Consume the builder and create a DataType.
317    pub const fn build(self) -> DataType {
318        let mut flags = 0_usize as VALUE;
319        if self.free_immediately {
320            flags |= RUBY_TYPED_FREE_IMMEDIATELY as VALUE;
321        }
322        if self.wb_protected || !self.mark {
323            flags |= RUBY_TYPED_WB_PROTECTED as VALUE;
324        }
325        #[cfg(ruby_gte_3_0)]
326        if self.frozen_shareable {
327            flags |= rbimpl_typeddata_flags::RUBY_TYPED_FROZEN_SHAREABLE as VALUE;
328        }
329        let dmark = if self.mark {
330            Some(T::extern_mark as _)
331        } else {
332            None
333        };
334        let dfree = Some(T::extern_free as _);
335        let dsize = if self.size {
336            Some(T::extern_size as _)
337        } else {
338            None
339        };
340        let dcompact = if self.compact {
341            Some(T::extern_compact as _)
342        } else {
343            None
344        };
345        DataType(rb_data_type_t {
346            wrap_struct_name: self.name.as_ptr() as _,
347            function: rb_data_type_struct__bindgen_ty_1 {
348                dmark,
349                dfree,
350                dsize,
351                dcompact,
352                reserved: [ptr::null_mut(); 1],
353            },
354            parent: ptr::null(),
355            data: ptr::null_mut(),
356            flags,
357        })
358    }
359}
360
361impl<T> DataTypeBuilder<T>
362where
363    T: DataTypeFunctions + Sync,
364{
365    /// Enable the 'frozen_shareable' flag.
366    ///
367    /// Set this if your type is thread safe when the Ruby wrapper object is
368    /// frozen.
369    pub const fn frozen_shareable(mut self) -> Self {
370        self.frozen_shareable = true;
371        self
372    }
373}
374
375/// A trait for Rust types that can be used with the
376/// `rb_data_typed_object_wrap` API.
377///
378/// # Safety
379///
380/// This trait is unsafe to implement as the fields of [`DataType`] returned by
381/// [`TypedData::data_type`] control low level behaviour that can go very wrong
382/// if set incorrectly. Implementing this trait is the only way a [`DataType`]
383/// can be passed to Ruby and result in safety violations, [`DataType`] is
384/// otherwise safe (but useless) to create.
385///
386/// The [`TypedData`](`derive@crate::TypedData`) or [`wrap`](`crate::wrap`)
387/// macros can help implementing this trait more safely.
388pub unsafe trait TypedData
389where
390    Self: Send + Sized,
391{
392    /// Should return the class for the Ruby object wrapping the Rust type.
393    ///
394    /// This can be overridden on a case by case basis by implementing
395    /// [`TypedData::class_for`], but the result of this function will always
396    /// be used in error messages if a value fails to convert to `Self`.
397    ///
398    /// If using [`class_for`](Self::class_for) it is advised to have this
399    /// function return the superclass of those returned by `class_for`.
400    ///
401    /// # Examples
402    ///
403    /// ```
404    /// use magnus::{prelude::*, value::Lazy, RClass, Ruby, TypedData};
405    /// # use magnus::DataType;
406    ///
407    /// struct Example();
408    ///
409    /// unsafe impl TypedData for Example {
410    ///     fn class(ruby: &Ruby) -> RClass {
411    ///         static CLASS: Lazy<RClass> = Lazy::new(|ruby| {
412    ///             let class = ruby.define_class("Example", ruby.class_object()).unwrap();
413    ///             class.undef_default_alloc_func();
414    ///             class
415    ///         });
416    ///         ruby.get_inner(&CLASS)
417    ///     }
418    ///
419    ///     // ...
420    /// #   fn data_type() -> &'static DataType { unimplemented!() }
421    /// }
422    /// ```
423    fn class(ruby: &Ruby) -> RClass;
424
425    /// Should return a static reference to a [`DataType`] with metadata about
426    /// the wrapped type.
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// use magnus::{data_type_builder, DataType, DataTypeFunctions, TypedData};
432    /// # use magnus::{RClass, Ruby};
433    ///
434    /// #[derive(DataTypeFunctions)]
435    /// struct Example();
436    ///
437    /// unsafe impl TypedData for Example {
438    /// #   fn class(_: &Ruby) -> RClass { unimplemented!() }
439    ///     // ...
440    ///
441    ///     fn data_type() -> &'static DataType {
442    ///         static DATA_TYPE: DataType = data_type_builder!(Example, "example").build();
443    ///         &DATA_TYPE
444    ///     }
445    /// }
446    /// ```
447    fn data_type() -> &'static DataType;
448
449    /// Used to customise the class wrapping a specific value of `Self`.
450    ///
451    /// The provided implementation simply returns the value of
452    /// [`TypedData::class`].
453    ///
454    /// The classes returned by this function must be subclasses of
455    /// `TypedData::class`. `TypedData::class` will always be used in error
456    /// messages if a value fails to convert to `Self`.
457    ///
458    /// See also [`Obj::wrap_as`]/[`RTypedData::wrap_as`].
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use magnus::{prelude::*, value::Lazy, RClass, Ruby, TypedData};
464    /// # use magnus::DataType;
465    ///
466    /// enum Example {
467    ///     A,
468    ///     B,
469    /// }
470    ///
471    /// unsafe impl TypedData for Example {
472    /// #   fn class(_: &Ruby) -> RClass { unimplemented!() }
473    /// #   fn data_type() -> &'static DataType { unimplemented!() }
474    ///     // ...
475    ///
476    ///     fn class_for(ruby: &Ruby, value: &Self) -> RClass {
477    ///         static A: Lazy<RClass> = Lazy::new(|ruby| {
478    ///             let class = ruby.define_class("A", Example::class(ruby)).unwrap();
479    ///             class.undef_default_alloc_func();
480    ///             class
481    ///         });
482    ///         static B: Lazy<RClass> = Lazy::new(|ruby| {
483    ///             let class = ruby.define_class("B", Example::class(ruby)).unwrap();
484    ///             class.undef_default_alloc_func();
485    ///             class
486    ///         });
487    ///         match value {
488    ///             Self::A => ruby.get_inner(&A),
489    ///             Self::B => ruby.get_inner(&B),
490    ///         }
491    ///     }
492    /// }
493    /// # let _ = (Example::A, Example::B);
494    /// ```
495    #[allow(unused_variables)]
496    fn class_for(ruby: &Ruby, value: &Self) -> RClass {
497        Self::class(ruby)
498    }
499}
500
501impl<T> TryConvert for &T
502where
503    T: TypedData,
504{
505    fn try_convert(val: Value) -> Result<Self, Error> {
506        let handle = Ruby::get_with(val);
507        unsafe {
508            RTypedData::from_value(val)
509                .ok_or_else(|| {
510                    Error::new(
511                        handle.exception_type_error(),
512                        format!(
513                            "no implicit conversion of {} into {}",
514                            val.classname(),
515                            T::class(&handle)
516                        ),
517                    )
518                })?
519                .get_unconstrained()
520        }
521    }
522}
523
524impl<T> IntoValue for T
525where
526    T: TypedData,
527{
528    #[inline]
529    fn into_value_with(self, handle: &Ruby) -> Value {
530        handle.wrap(self).into_value_with(handle)
531    }
532}
533
534/// A Ruby Object wrapping a Rust type `T`.
535///
536/// This is a Value pointer to a RTypedData struct, Ruby’s internal
537/// representation of objects that wrap foreign types. Unlike [`RTypedData`] it
538/// tracks the Rust type it should contains and errors early in [`TryConvert`]
539/// if types don't match.
540///
541/// See the [`ReprValue`] and [`Object`] traits for additional methods
542/// available on this type. See [`Ruby`](Ruby#typed_dataobj) for methods to
543/// create a `typed_data::Obj`.
544#[repr(transparent)]
545pub struct Obj<T> {
546    inner: RTypedData,
547    phantom: PhantomData<T>,
548}
549
550impl<T> Copy for Obj<T> where T: TypedData {}
551
552impl<T> Clone for Obj<T>
553where
554    T: TypedData,
555{
556    fn clone(&self) -> Self {
557        *self
558    }
559}
560
561/// # `typed_data::Obj`
562///
563/// Functions to wrap Rust data in a Ruby object.
564///
565/// See also [`RTypedData`](Ruby#rtypeddata) and the [`typed_data::Obj`](Obj)
566/// type.
567impl Ruby {
568    /// Wrap the Rust type `T` in a Ruby object.
569    ///
570    /// # Examples
571    ///
572    /// ```
573    /// use magnus::{prelude::*, Error, Ruby};
574    ///
575    /// #[magnus::wrap(class = "Point")]
576    /// struct Point {
577    ///     x: isize,
578    ///     y: isize,
579    /// }
580    ///
581    /// fn example(ruby: &Ruby) -> Result<(), Error> {
582    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
583    ///
584    ///     let value = ruby.obj_wrap(Point { x: 4, y: 2 });
585    ///     assert!(value.is_kind_of(point_class));
586    ///
587    ///     Ok(())
588    /// }
589    /// # Ruby::init(example).unwrap();
590    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
591    /// ```
592    pub fn obj_wrap<T>(&self, data: T) -> Obj<T>
593    where
594        T: TypedData,
595    {
596        Obj {
597            inner: self.wrap(data),
598            phantom: PhantomData,
599        }
600    }
601
602    /// Wrap the Rust type `T` in a Ruby object that is an instance of the
603    /// given `class`.
604    ///
605    /// See also [`TypedData::class_for`].
606    ///
607    /// # Panics
608    ///
609    /// Panics if `class` is not a subclass of `<T as TypedData>::class()`.
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// use magnus::{prelude::*, Error, Ruby};
615    ///
616    /// #[magnus::wrap(class = "Point")]
617    /// struct Point {
618    ///     x: isize,
619    ///     y: isize,
620    /// }
621    ///
622    /// fn example(ruby: &Ruby) -> Result<(), Error> {
623    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
624    ///     let point_sub_class = ruby.define_class("SubPoint", point_class)?;
625    ///
626    ///     let value = ruby.obj_wrap_as(Point { x: 4, y: 2 }, point_sub_class);
627    ///     assert!(value.is_kind_of(point_sub_class));
628    ///     assert!(value.is_kind_of(point_class));
629    ///
630    ///     Ok(())
631    /// }
632    /// # Ruby::init(example).unwrap();
633    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
634    /// ```
635    ///
636    /// Allowing a wrapped type to be subclassed from Ruby:
637    ///
638    /// (note, in this example `Point` does not have and does not call the
639    /// `initialize` method, subclasses would need to override the class `new`
640    /// method rather than `initialize`)
641    ///
642    /// ```
643    /// use magnus::{function, method, prelude::*, typed_data, Error, RClass, Ruby, Value};
644    ///
645    /// #[magnus::wrap(class = "Point")]
646    /// struct Point {
647    ///     x: isize,
648    ///     y: isize,
649    /// }
650    ///
651    /// impl Point {
652    ///     fn new(ruby: &Ruby, class: RClass, x: isize, y: isize) -> typed_data::Obj<Self> {
653    ///         ruby.obj_wrap_as(Self { x, y }, class)
654    ///     }
655    /// }
656    ///
657    /// fn example(ruby: &Ruby) -> Result<(), Error> {
658    ///     let point_class = ruby.define_class("Point", ruby.class_object())?;
659    ///     point_class.define_singleton_method("new", method!(Point::new, 2))?;
660    ///     point_class
661    ///         .define_singleton_method("inherited", function!(RClass::undef_default_alloc_func, 1))?;
662    ///
663    ///     let value: Value = ruby.eval(
664    ///         r#"
665    ///           class SubPoint < Point
666    ///           end
667    ///           SubPoint.new(4, 2)
668    ///         "#,
669    ///     )?;
670    ///
671    ///     assert!(value.is_kind_of(ruby.class_object().const_get::<_, RClass>("SubPoint")?));
672    ///     assert!(value.is_kind_of(point_class));
673    ///
674    ///     Ok(())
675    /// }
676    /// # Ruby::init(example).unwrap();
677    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
678    /// ```
679    pub fn obj_wrap_as<T>(&self, data: T, class: RClass) -> Obj<T>
680    where
681        T: TypedData,
682    {
683        Obj {
684            inner: self.wrap_as(data, class),
685            phantom: PhantomData,
686        }
687    }
688}
689
690impl<T> Obj<T>
691where
692    T: TypedData,
693{
694    /// Wrap the Rust type `T` in a Ruby object.
695    ///
696    /// # Panics
697    ///
698    /// Panics if called from a non-Ruby thread. See [`Ruby::obj_wrap`] for the
699    /// non-panicking version.
700    ///
701    /// # Examples
702    ///
703    /// ```
704    /// # #![allow(deprecated)]
705    /// use magnus::{class, define_class, prelude::*, typed_data};
706    /// # let _cleanup = unsafe { magnus::embed::init() };
707    ///
708    /// #[magnus::wrap(class = "Point")]
709    /// struct Point {
710    ///     x: isize,
711    ///     y: isize,
712    /// }
713    ///
714    /// let point_class = define_class("Point", class::object()).unwrap();
715    ///
716    /// let value = typed_data::Obj::wrap(Point { x: 4, y: 2 });
717    /// assert!(value.is_kind_of(point_class));
718    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
719    /// ```
720    #[cfg_attr(
721        not(feature = "old-api"),
722        deprecated(note = "please use `Ruby::obj_wrap` instead")
723    )]
724    #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
725    #[inline]
726    pub fn wrap(data: T) -> Self {
727        get_ruby!().obj_wrap(data)
728    }
729
730    /// Wrap the Rust type `T` in a Ruby object that is an instance of the
731    /// given `class`.
732    ///
733    /// See also [`TypedData::class_for`].
734    ///
735    /// # Panics
736    ///
737    /// Panics if `class` is not a subclass of `<T as TypedData>::class()`, or
738    /// if called from a non-Ruby thread. See [`Ruby::obj_wrap_as`] for a
739    /// version that can not be called from a non-Ruby thread, so will not
740    /// panic for that reason.
741    ///
742    /// # Examples
743    ///
744    /// ```
745    /// # #![allow(deprecated)]
746    /// use magnus::{class, define_class, prelude::*, typed_data};
747    /// # let _cleanup = unsafe { magnus::embed::init() };
748    ///
749    /// #[magnus::wrap(class = "Point")]
750    /// struct Point {
751    ///     x: isize,
752    ///     y: isize,
753    /// }
754    ///
755    /// let point_class = define_class("Point", class::object()).unwrap();
756    /// let point_sub_class = define_class("SubPoint", point_class).unwrap();
757    ///
758    /// let value = typed_data::Obj::wrap_as(Point { x: 4, y: 2 }, point_sub_class);
759    /// assert!(value.is_kind_of(point_sub_class));
760    /// assert!(value.is_kind_of(point_class));
761    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
762    /// ```
763    ///
764    /// Allowing a wrapped type to be subclassed from Ruby:
765    ///
766    /// (note, in this example `Point` does not have and does not call
767    /// the `initialize` method, subclasses would need to override the class
768    /// `new` method rather than `initialize`)
769    ///
770    /// ```
771    /// # #![allow(deprecated)]
772    /// use magnus::{
773    ///     class, define_class, eval, function, method, prelude::*, typed_data, RClass, Value,
774    /// };
775    /// # let _cleanup = unsafe { magnus::embed::init() };
776    ///
777    /// #[magnus::wrap(class = "Point")]
778    /// struct Point {
779    ///     x: isize,
780    ///     y: isize,
781    /// }
782    ///
783    /// impl Point {
784    ///     fn new(class: RClass, x: isize, y: isize) -> typed_data::Obj<Self> {
785    ///         typed_data::Obj::wrap_as(Self { x, y }, class)
786    ///     }
787    /// }
788    /// let point_class = define_class("Point", class::object()).unwrap();
789    /// point_class
790    ///     .define_singleton_method("new", method!(Point::new, 2))
791    ///     .unwrap();
792    /// point_class
793    ///     .define_singleton_method("inherited", function!(RClass::undef_default_alloc_func, 1))
794    ///     .unwrap();
795    ///
796    /// let value: Value = eval(
797    ///     r#"
798    ///       class SubPoint < Point
799    ///       end
800    ///       SubPoint.new(4, 2)
801    ///     "#,
802    /// )
803    /// .unwrap();
804    ///
805    /// assert!(value.is_kind_of(class::object().const_get::<_, RClass>("SubPoint").unwrap()));
806    /// assert!(value.is_kind_of(point_class));
807    /// # let _ = Point { x: 1, y: 2 }.x + Point { x: 3, y: 4 }.y;
808    /// ```
809    #[cfg_attr(
810        not(feature = "old-api"),
811        deprecated(note = "please use `Ruby::obj_wrap_as` instead")
812    )]
813    #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
814    #[inline]
815    pub fn wrap_as(data: T, class: RClass) -> Self {
816        get_ruby!().obj_wrap_as(data, class)
817    }
818}
819
820impl<T> Deref for Obj<T>
821where
822    T: TypedData,
823{
824    type Target = T;
825
826    /// Dereference to the Rust type wrapped in the Ruby object `self`.
827    ///
828    /// # Examples
829    ///
830    /// ```
831    /// use magnus::{Error, Ruby};
832    ///
833    /// #[magnus::wrap(class = "Point")]
834    /// #[derive(Debug, PartialEq, Eq)]
835    /// struct Point {
836    ///     x: isize,
837    ///     y: isize,
838    /// }
839    ///
840    /// fn example(ruby: &Ruby) -> Result<(), Error> {
841    ///     ruby.define_class("Point", ruby.class_object())?;
842    ///     let value = ruby.obj_wrap(Point { x: 4, y: 2 });
843    ///
844    ///     assert_eq!(&*value, &Point { x: 4, y: 2 });
845    ///
846    ///     Ok(())
847    /// }
848    /// # Ruby::init(example).unwrap()
849    /// ```
850    fn deref(&self) -> &Self::Target {
851        // Since we've already validated the inner during `TryConvert` via `RTypedData::get`, we
852        // can skip the extra checks and libruby calls and just access the data directly.
853        unsafe {
854            let data_ptr = RTYPEDDATA_GET_DATA(self.inner.as_rb_value()) as *mut Self::Target;
855            &*data_ptr
856        }
857    }
858}
859
860impl<T> fmt::Display for Obj<T>
861where
862    T: TypedData,
863{
864    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865        write!(f, "{}", unsafe { self.to_s_infallible() })
866    }
867}
868
869impl<T> fmt::Debug for Obj<T>
870where
871    T: TypedData,
872{
873    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874        write!(f, "{}", self.inspect())
875    }
876}
877
878impl<T> IntoValue for Obj<T>
879where
880    T: TypedData,
881{
882    #[inline]
883    fn into_value_with(self, handle: &Ruby) -> Value {
884        self.inner.into_value_with(handle)
885    }
886}
887
888impl<T> From<Obj<T>> for RTypedData
889where
890    T: TypedData,
891{
892    fn from(val: Obj<T>) -> Self {
893        val.inner
894    }
895}
896
897impl<T> Object for Obj<T> where T: TypedData {}
898
899unsafe impl<T> private::ReprValue for Obj<T> where T: TypedData {}
900
901impl<T> ReprValue for Obj<T> where T: TypedData {}
902
903impl<T> TryConvert for Obj<T>
904where
905    T: TypedData,
906{
907    fn try_convert(val: Value) -> Result<Self, Error> {
908        let handle = Ruby::get_with(val);
909        let inner = RTypedData::from_value(val).ok_or_else(|| {
910            Error::new(
911                handle.exception_type_error(),
912                format!(
913                    "no implicit conversion of {} into {}",
914                    unsafe { val.classname() },
915                    T::class(&handle)
916                ),
917            )
918        })?;
919
920        // check it really does contain a T
921        inner.get::<T>()?;
922
923        Ok(Self {
924            inner,
925            phantom: PhantomData,
926        })
927    }
928}
929
930/// A trait for types that can be used with the `rb_gc_writebarrier` API.
931pub trait Writebarrier: ReprValue {
932    /// Inform Ruby that `self` contains a reference to `new`.
933    ///
934    /// If you have a Rust type that contains Ruby values, and itself is
935    /// wrapped as a Ruby object, and you choose to enable the `wb_protected`
936    /// flag so that it can participate in generational GC then all operations
937    /// that add a Ruby value to your data type must call this function.
938    ///
939    /// The write barrier is hard to get right. Magnus recommends you do not
940    /// enable the `wb_protected` flag, and thus you don't need to use this function.
941    ///
942    /// # Examples
943    ///
944    /// ```
945    /// use std::cell::RefCell;
946    ///
947    /// use magnus::{
948    ///     function, gc, method,
949    ///     prelude::*,
950    ///     typed_data::{Obj, Writebarrier},
951    ///     value::Opaque,
952    ///     DataTypeFunctions, Error, Ruby, TypedData, Value,
953    /// };
954    ///
955    /// #[derive(TypedData)]
956    /// #[magnus(class = "MyVec", free_immediately, mark, wb_protected)]
957    /// struct MyVec {
958    ///     values: RefCell<Vec<Opaque<Value>>>,
959    /// }
960    ///
961    /// impl DataTypeFunctions for MyVec {
962    ///     fn mark(&self, marker: &gc::Marker) {
963    ///         marker.mark_slice(self.values.borrow().as_slice());
964    ///     }
965    /// }
966    ///
967    /// impl MyVec {
968    ///     fn new() -> Self {
969    ///         Self {
970    ///             values: RefCell::new(Vec::new()),
971    ///         }
972    ///     }
973    ///
974    ///     fn push(ruby: &Ruby, rb_self: Obj<Self>, val: Value) -> Obj<Self> {
975    ///         rb_self.values.borrow_mut().push(val.into());
976    ///         rb_self.writebarrier(rb_self);
977    ///         rb_self
978    ///     }
979    /// }
980    ///
981    /// fn example(ruby: &Ruby) -> Result<(), Error> {
982    ///     let class = ruby.define_class("MyVec", ruby.class_object())?;
983    ///     class.define_singleton_method("new", function!(MyVec::new, 0))?;
984    ///     class.define_method("push", method!(MyVec::push, 1))?;
985    ///
986    ///     let _: Value = ruby.eval(
987    ///         r#"
988    ///             vec = MyVec.new
989    ///             vec.push("test")
990    ///             vec.push("example")
991    ///         "#,
992    ///     )?;
993    ///
994    ///     Ok(())
995    /// }
996    /// # Ruby::init(example).unwrap();
997    /// ```
998    fn writebarrier<T>(&self, young: T)
999    where
1000        T: Mark,
1001    {
1002        unsafe { rb_gc_writebarrier(self.as_rb_value(), young.raw()) };
1003    }
1004
1005    /// Opts `self` out of generational GC / write barrier protection.
1006    ///
1007    /// After calling this function `self` will not participate in generational
1008    /// GC and will always be scanned during a GC.
1009    /// See [`RTypedData::writebarrier`].
1010    fn writebarrier_unprotect<T, U>(&self) {
1011        unsafe { rb_gc_writebarrier_unprotect(self.as_rb_value()) };
1012    }
1013}
1014
1015impl Writebarrier for RTypedData {}
1016
1017impl<T> Writebarrier for Obj<T> where T: TypedData {}
1018
1019/// Trait for a Ruby-compatible `#hash` method.
1020///
1021/// Automatically implemented for any type implementing [`std::hash::Hash`].
1022///
1023/// See also [`Dup`], [`Inspect`], [`IsEql`], and [`typed_data::Cmp`](Cmp).
1024///
1025/// # Examples
1026///
1027/// ```
1028/// use std::hash::Hasher;
1029///
1030/// use magnus::{
1031///     function, gc, method, prelude::*, typed_data, value::Opaque, DataTypeFunctions, Error,
1032///     Ruby, TypedData, Value,
1033/// };
1034///
1035/// #[derive(TypedData)]
1036/// #[magnus(class = "Pair", free_immediately, mark)]
1037/// struct Pair {
1038///     #[magnus(opaque_attr_reader)]
1039///     a: Opaque<Value>,
1040///     #[magnus(opaque_attr_reader)]
1041///     b: Opaque<Value>,
1042/// }
1043///
1044/// impl Pair {
1045///     fn new(a: Value, b: Value) -> Self {
1046///         Self {
1047///             a: a.into(),
1048///             b: b.into(),
1049///         }
1050///     }
1051/// }
1052///
1053/// impl DataTypeFunctions for Pair {
1054///     fn mark(&self, marker: &gc::Marker) {
1055///         marker.mark(self.a);
1056///         marker.mark(self.b);
1057///     }
1058/// }
1059///
1060/// impl std::hash::Hash for Pair {
1061///     fn hash<H: Hasher>(&self, state: &mut H) {
1062///         state.write_i64(
1063///             self.a()
1064///                 .hash()
1065///                 .expect("#hash should not fail")
1066///                 .to_i64()
1067///                 .expect("#hash result guaranteed to be <= i64"),
1068///         );
1069///         state.write_i64(
1070///             self.b()
1071///                 .hash()
1072///                 .expect("#hash should not fail")
1073///                 .to_i64()
1074///                 .expect("#hash result guaranteed to be <= i64"),
1075///         );
1076///     }
1077/// }
1078///
1079/// impl PartialEq for Pair {
1080///     fn eq(&self, other: &Self) -> bool {
1081///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1082///     }
1083/// }
1084///
1085/// impl Eq for Pair {}
1086///
1087/// fn example(ruby: &Ruby) -> Result<(), Error> {
1088///     let class = ruby.define_class("Pair", ruby.class_object())?;
1089///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1090///     class.define_method("hash", method!(<Pair as typed_data::Hash>::hash, 0))?;
1091///     class.define_method("eql?", method!(<Pair as typed_data::IsEql>::is_eql, 1))?;
1092///
1093///     let a = Pair::new(
1094///         ruby.str_new("foo").as_value(),
1095///         ruby.integer_from_i64(1).as_value(),
1096///     );
1097///     let hash = ruby.hash_new();
1098///     hash.aset(a, "test value")?;
1099///
1100///     let b = Pair::new(
1101///         ruby.str_new("foo").as_value(),
1102///         ruby.integer_from_i64(1).as_value(),
1103///     );
1104///     assert_eq!("test value", hash.fetch::<_, String>(b)?);
1105///
1106///     let c = Pair::new(
1107///         ruby.str_new("bar").as_value(),
1108///         ruby.integer_from_i64(2).as_value(),
1109///     );
1110///     assert!(hash.get(c).is_none());
1111///
1112///     Ok(())
1113/// }
1114/// # Ruby::init(example).unwrap()
1115/// ```
1116pub trait Hash {
1117    // Docs at trait level.
1118    #![allow(missing_docs)]
1119    fn hash(&self) -> i64;
1120}
1121
1122impl<T> Hash for T
1123where
1124    T: std::hash::Hash,
1125{
1126    fn hash(&self) -> i64 {
1127        let mut hasher = DefaultHasher::new();
1128        std::hash::Hash::hash(self, &mut hasher);
1129        // Ensure the Rust usize hash converts nicely to Ruby's expected range
1130        // if we return usize it'd truncate to 0 for anything negative.
1131        hasher.finish() as i64
1132    }
1133}
1134
1135/// Trait for a Ruby-compatible `#eql?` method.
1136///
1137/// Automatically implemented for any type implementing [`Eq`] and
1138/// [`TryConvert`].
1139///
1140/// See also [`Dup`], [`Inspect`], [`typed_data::Cmp`](Cmp), and
1141/// [`typed_data::Hash`](Hash).
1142///
1143/// # Examples
1144///
1145/// ```
1146/// use std::hash::Hasher;
1147///
1148/// use magnus::{
1149///     function, gc, method, prelude::*, typed_data, value::Opaque, DataTypeFunctions, Error,
1150///     Ruby, TypedData, Value,
1151/// };
1152///
1153/// #[derive(TypedData)]
1154/// #[magnus(class = "Pair", free_immediately, mark)]
1155/// struct Pair {
1156///     #[magnus(opaque_attr_reader)]
1157///     a: Opaque<Value>,
1158///     #[magnus(opaque_attr_reader)]
1159///     b: Opaque<Value>,
1160/// }
1161///
1162/// impl Pair {
1163///     fn new(a: Value, b: Value) -> Self {
1164///         Self {
1165///             a: a.into(),
1166///             b: b.into(),
1167///         }
1168///     }
1169/// }
1170///
1171/// impl DataTypeFunctions for Pair {
1172///     fn mark(&self, marker: &gc::Marker) {
1173///         marker.mark(self.a);
1174///         marker.mark(self.b);
1175///     }
1176/// }
1177///
1178/// impl std::hash::Hash for Pair {
1179///     fn hash<H: Hasher>(&self, state: &mut H) {
1180///         state.write_i64(
1181///             self.a()
1182///                 .hash()
1183///                 .expect("#hash should not fail")
1184///                 .to_i64()
1185///                 .expect("#hash result guaranteed to be <= i64"),
1186///         );
1187///         state.write_i64(
1188///             self.b()
1189///                 .hash()
1190///                 .expect("#hash should not fail")
1191///                 .to_i64()
1192///                 .expect("#hash result guaranteed to be <= i64"),
1193///         );
1194///     }
1195/// }
1196///
1197/// impl PartialEq for Pair {
1198///     fn eq(&self, other: &Self) -> bool {
1199///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1200///     }
1201/// }
1202///
1203/// impl Eq for Pair {}
1204///
1205/// fn example(ruby: &Ruby) -> Result<(), Error> {
1206///     let class = ruby.define_class("Pair", ruby.class_object())?;
1207///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1208///     class.define_method("hash", method!(<Pair as typed_data::Hash>::hash, 0))?;
1209///     class.define_method("eql?", method!(<Pair as typed_data::IsEql>::is_eql, 1))?;
1210///
1211///     let a = Pair::new(
1212///         ruby.str_new("foo").as_value(),
1213///         ruby.integer_from_i64(1).as_value(),
1214///     );
1215///     let hash = ruby.hash_new();
1216///     hash.aset(a, "test value")?;
1217///
1218///     let b = Pair::new(
1219///         ruby.str_new("foo").as_value(),
1220///         ruby.integer_from_i64(1).as_value(),
1221///     );
1222///     assert_eq!("test value", hash.fetch::<_, String>(b)?);
1223///
1224///     let c = Pair::new(
1225///         ruby.str_new("bar").as_value(),
1226///         ruby.integer_from_i64(2).as_value(),
1227///     );
1228///     assert!(hash.get(c).is_none());
1229///
1230///     Ok(())
1231/// }
1232/// # Ruby::init(example).unwrap()
1233/// ```
1234pub trait IsEql {
1235    // Docs at trait level.
1236    #![allow(missing_docs)]
1237    fn is_eql(&self, other: Value) -> bool;
1238}
1239
1240impl<'a, T> IsEql for T
1241where
1242    T: Eq + 'a,
1243    &'a T: TryConvert,
1244{
1245    fn is_eql(&self, other: Value) -> bool {
1246        <&'a T>::try_convert(other)
1247            .map(|o| self == o)
1248            .unwrap_or(false)
1249    }
1250}
1251
1252/// Trait for a Ruby-compatible `#<=>` method.
1253///
1254/// Automatically implemented for any type implementing [`PartialOrd`] and
1255/// [`TryConvert`].
1256///
1257/// See also [`Dup`], [`Inspect`], [`IsEql`] and [`typed_data::Hash`](Hash).
1258///
1259/// # Examples
1260///
1261/// ```
1262/// use std::cmp::Ordering;
1263///
1264/// use magnus::{
1265///     function, gc, method, prelude::*, rb_assert, typed_data, value::Opaque, DataTypeFunctions,
1266///     Error, Module, Ruby, TypedData, Value,
1267/// };
1268///
1269/// #[derive(TypedData)]
1270/// #[magnus(class = "Pair", free_immediately, mark)]
1271/// struct Pair {
1272///     #[magnus(opaque_attr_reader)]
1273///     a: Opaque<Value>,
1274///     #[magnus(opaque_attr_reader)]
1275///     b: Opaque<Value>,
1276/// }
1277///
1278/// impl Pair {
1279///     fn new(a: Value, b: Value) -> Self {
1280///         Self {
1281///             a: a.into(),
1282///             b: b.into(),
1283///         }
1284///     }
1285/// }
1286///
1287/// impl DataTypeFunctions for Pair {
1288///     fn mark(&self, marker: &gc::Marker) {
1289///         marker.mark(self.a);
1290///         marker.mark(self.b);
1291///     }
1292/// }
1293///
1294/// impl PartialEq for Pair {
1295///     fn eq(&self, other: &Self) -> bool {
1296///         self.a().eql(other.a()).unwrap_or(false) && self.b().eql(other.b()).unwrap_or(false)
1297///     }
1298/// }
1299///
1300/// impl PartialOrd for Pair {
1301///     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1302///         let a = self
1303///             .a()
1304///             .funcall("<=>", (other.a(),))
1305///             .ok()
1306///             .map(|o: i64| o.cmp(&0))?;
1307///         match a {
1308///             Ordering::Less | Ordering::Greater => Some(a),
1309///             Ordering::Equal => self
1310///                 .b()
1311///                 .funcall("<=>", (other.b(),))
1312///                 .ok()
1313///                 .map(|o: i64| o.cmp(&0)),
1314///         }
1315///     }
1316/// }
1317///
1318/// fn example(ruby: &Ruby) -> Result<(), Error> {
1319///     let class = ruby.define_class("Pair", ruby.class_object())?;
1320///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1321///     class.define_method("<=>", method!(<Pair as typed_data::Cmp>::cmp, 1))?;
1322///     class.include_module(ruby.module_comparable())?;
1323///
1324///     let a = Pair::new(
1325///         ruby.str_new("foo").as_value(),
1326///         ruby.integer_from_i64(1).as_value(),
1327///     );
1328///     let b = Pair::new(
1329///         ruby.str_new("foo").as_value(),
1330///         ruby.integer_from_i64(2).as_value(),
1331///     );
1332///     rb_assert!(ruby, "a < b", a, b);
1333///
1334///     let b = Pair::new(
1335///         ruby.str_new("foo").as_value(),
1336///         ruby.integer_from_i64(2).as_value(),
1337///     );
1338///     let c = Pair::new(
1339///         ruby.str_new("bar").as_value(),
1340///         ruby.integer_from_i64(1).as_value(),
1341///     );
1342///     rb_assert!(ruby, "b > c", b, c);
1343///
1344///     let a = Pair::new(
1345///         ruby.str_new("foo").as_value(),
1346///         ruby.integer_from_i64(1).as_value(),
1347///     );
1348///     let b = Pair::new(
1349///         ruby.str_new("foo").as_value(),
1350///         ruby.integer_from_i64(2).as_value(),
1351///     );
1352///     rb_assert!(ruby, "(a <=> b) == -1", a, b);
1353///
1354///     Ok(())
1355/// }
1356/// # Ruby::init(example).unwrap()
1357/// ```
1358pub trait Cmp {
1359    // Docs at trait level.
1360    #![allow(missing_docs)]
1361    fn cmp(&self, other: Value) -> Option<i64>;
1362}
1363
1364impl<'a, T> Cmp for T
1365where
1366    T: PartialOrd + 'a,
1367    &'a T: TryConvert,
1368{
1369    fn cmp(&self, other: Value) -> Option<i64> {
1370        <&'a T>::try_convert(other)
1371            .ok()
1372            .and_then(|o| self.partial_cmp(o))
1373            .map(|o| o as i64)
1374    }
1375}
1376
1377/// Trait for a Ruby-compatible `#inspect` method.
1378///
1379/// Automatically implemented for any type implementing [`Debug`].
1380///
1381/// See also [`Dup`], [`IsEql`], [`typed_data::Cmp`](Cmp), and
1382/// [`typed_data::Hash`](Hash).
1383///
1384/// # Examples
1385///
1386/// ```
1387/// use std::fmt;
1388///
1389/// use magnus::{
1390///     function, gc, method, prelude::*, rb_assert, typed_data, value::Opaque, DataTypeFunctions,
1391///     Error, Ruby, TypedData, Value,
1392/// };
1393///
1394/// #[derive(TypedData)]
1395/// #[magnus(class = "Pair", free_immediately, mark)]
1396/// struct Pair {
1397///     #[magnus(opaque_attr_reader)]
1398///     a: Opaque<Value>,
1399///     #[magnus(opaque_attr_reader)]
1400///     b: Opaque<Value>,
1401/// }
1402///
1403/// impl Pair {
1404///     fn new(a: Value, b: Value) -> Self {
1405///         Self {
1406///             a: a.into(),
1407///             b: b.into(),
1408///         }
1409///     }
1410/// }
1411///
1412/// impl DataTypeFunctions for Pair {
1413///     fn mark(&self, marker: &gc::Marker) {
1414///         marker.mark(self.a);
1415///         marker.mark(self.b);
1416///     }
1417/// }
1418///
1419/// impl fmt::Debug for Pair {
1420///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1421///         f.debug_struct("Pair")
1422///             .field("a", &self.a())
1423///             .field("b", &self.b())
1424///             .finish()
1425///     }
1426/// }
1427///
1428/// fn example(ruby: &Ruby) -> Result<(), Error> {
1429///     let class = ruby.define_class("Pair", ruby.class_object())?;
1430///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1431///     class.define_method(
1432///         "inspect",
1433///         method!(<Pair as typed_data::Inspect>::inspect, 0),
1434///     )?;
1435///
1436///     let pair = Pair::new(
1437///         ruby.str_new("foo").as_value(),
1438///         ruby.integer_from_i64(1).as_value(),
1439///     );
1440///     rb_assert!(ruby, r#"pair.inspect == "Pair { a: \"foo\", b: 1 }""#, pair);
1441///
1442///     Ok(())
1443/// }
1444/// # Ruby::init(example).unwrap()
1445/// ```
1446pub trait Inspect {
1447    // Docs at trait level.
1448    #![allow(missing_docs)]
1449    fn inspect(&self) -> String;
1450}
1451
1452impl<T> Inspect for T
1453where
1454    T: fmt::Debug,
1455{
1456    fn inspect(&self) -> String {
1457        format!("{:?}", self)
1458    }
1459}
1460
1461/// Trait for a Ruby-compatible `#dup` and `#clone` methods.
1462///
1463/// Automatically implemented for any type implementing [`Clone`].
1464///
1465/// See also [`Inspect`], [`IsEql`], [`typed_data::Cmp`](Cmp), and
1466/// [`typed_data::Hash`](Hash).
1467///
1468/// # Examples
1469///
1470/// ```
1471/// use magnus::{
1472///     function, gc, method, prelude::*, rb_assert, typed_data, value::Opaque, DataTypeFunctions,
1473///     Error, Ruby, TypedData, Value,
1474/// };
1475///
1476/// #[derive(TypedData, Clone)]
1477/// #[magnus(class = "Pair", free_immediately, mark)]
1478/// struct Pair {
1479///     a: Opaque<Value>,
1480///     b: Opaque<Value>,
1481/// }
1482///
1483/// impl Pair {
1484///     fn new(a: Value, b: Value) -> Self {
1485///         Self {
1486///             a: a.into(),
1487///             b: b.into(),
1488///         }
1489///     }
1490/// }
1491///
1492/// impl DataTypeFunctions for Pair {
1493///     fn mark(&self, marker: &gc::Marker) {
1494///         marker.mark(self.a);
1495///         marker.mark(self.b);
1496///     }
1497/// }
1498///
1499/// fn example(ruby: &Ruby) -> Result<(), Error> {
1500///     let class = ruby.define_class("Pair", ruby.class_object())?;
1501///     class.define_singleton_method("new", function!(Pair::new, 2))?;
1502///     class.define_method("dup", method!(<Pair as typed_data::Dup>::dup, 0))?;
1503///     class.define_method("clone", method!(<Pair as typed_data::Dup>::clone, -1))?;
1504///
1505///     let a = Pair::new(
1506///         ruby.str_new("foo").as_value(),
1507///         ruby.integer_from_i64(1).as_value(),
1508///     );
1509///     rb_assert!(ruby, "b = a.dup; a.object_id != b.object_id", a);
1510///
1511///     Ok(())
1512/// }
1513/// # Ruby::init(example).unwrap()
1514/// ```
1515pub trait Dup: Sized {
1516    // Docs at trait level.
1517    #![allow(missing_docs)]
1518    fn dup(&self) -> Self;
1519    fn clone(rbself: Obj<Self>, args: &[Value]) -> Result<Obj<Self>, Error>;
1520}
1521
1522impl<T> Dup for T
1523where
1524    T: Clone + TypedData,
1525{
1526    fn dup(&self) -> Self {
1527        self.clone()
1528    }
1529
1530    fn clone(rbself: Obj<Self>, args: &[Value]) -> Result<Obj<Self>, Error> {
1531        let args = scan_args::<(), (), (), (), _, ()>(args)?;
1532        let kwargs =
1533            get_kwargs::<_, (), (Option<Option<bool>>,), ()>(args.keywords, &[], &["freeze"])?;
1534        let (freeze,) = kwargs.optional;
1535        let freeze = freeze.flatten();
1536
1537        let clone = Ruby::get_with(rbself).obj_wrap((*rbself).clone());
1538        let class_clone = unsafe { rb_singleton_class_clone(rbself.as_rb_value()) };
1539        unsafe { rb_obj_reveal(clone.as_rb_value(), class_clone) };
1540        unsafe { rb_singleton_class_attached(class_clone, clone.as_rb_value()) };
1541        match freeze {
1542            Some(true) => clone.freeze(),
1543            None if rbself.is_frozen() => clone.freeze(),
1544            _ => (),
1545        }
1546        Ok(clone)
1547    }
1548}