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}