jlrs/convert/
unbox.rs

1//! Unbox the contents of a Julia value.
2//!
3//! A [`Value`] contains a pointer to some data owned by Julia. The layout of this data depends on
4//! its [`DataType`]. It's often possible to provide a type defined in Rust that matches the
5//! layout of the data in Julia. For example, if the `DataType` is `Int8`, the pointer points to
6//! an `i8`. Extracting the contents of a `Value` is called unboxing.
7//!
8//! The [`Unbox`] trait defined in this module usually dereferences the pointer. There are a few
9//! exceptions to this rule. In particular, unboxing a `char` or a `bool` results in a [`Char`] or
10//! a [`Bool`] respectively. The reason is that while using invalid `Char`s and `Bool`s is an
11//! error in Julia, it's undefined behavior to create them in Rust. Similarly, strings in Julia
12//! should be UTF-8 encoded, but to account for the possibility that the contents are invalid the
13//! implementation of `Unbox` returns a `Result<String, Vec<u8>>`.
14//!
15//! Unlike [`IntoJulia`], the `Unbox` trait is not limited to bits-types. The only requirement is
16//! that the layout of the types in both languages match. Types that can be unboxed include those
17//! with pointer fields, type parameters, and bits unions. When layouts are generated with
18//! JlrsReflect.jl [`Unbox`] is always derived.
19//!
20//! [`Bool`]: crate::data::layout::bool::Bool
21//! [`Char`]: crate::data::layout::char::Char
22//! [`DataType`]: crate::data::managed::datatype::DataType
23//! [`IntoJulia`]: crate::convert::into_julia::IntoJulia
24
25use std::ffi::c_void;
26
27use jl_sys::{
28    jl_unbox_float32, jl_unbox_float64, jl_unbox_int8, jl_unbox_int16, jl_unbox_int32,
29    jl_unbox_int64, jl_unbox_uint8, jl_unbox_uint16, jl_unbox_uint32, jl_unbox_uint64,
30    jl_unbox_voidpointer,
31};
32use jlrs_sys::{jlrs_unbox_long, jlrs_unbox_ulong};
33
34use super::into_julia::IntoJulia;
35use crate::data::managed::value::Value;
36
37/// A trait implemented by types that can be extracted from a Julia value with [`Value::unbox`].
38///
39/// This trait can be derived, it's recommended
40/// to use JlrsReflect.jl to ensure it's implemented correctly. All layouts generated by
41/// JlrsReflect.jl will implement this trait and [`ValidLayout`], which checks if the conversion
42/// is valid at runtime.
43///
44/// If you do choose to implement it manually, you only need to provide the associated `Output`
45/// type if the type matches the layout of the data in Julia. The default implementation of
46/// `unbox` dereferences the value as `&Self::Output` and clones it. If this implementation is
47/// incorrect it must be overridden.
48///
49/// [`Value::unbox`]: crate::data::managed::value::Value::unbox
50/// [`ValidLayout`]: crate::data::layout::valid_layout::ValidLayout
51#[diagnostic::on_unimplemented(
52    message = "the trait bound `{Self}: Unbox` is not satisfied",
53    label = "the trait `Unbox` is not implemented for `{Self}`",
54    note = "Custom types that implement `Unbox` should be generated with JlrsCore.reflect",
55    note = "Do not implement `ForeignType` or `OpaqueType` unless this type is exported to Julia with `julia_module!`"
56)]
57pub unsafe trait Unbox {
58    /// The type of the unboxed data. Must be `#[repr(C)]`.
59    type Output: Sized + Clone;
60
61    /// Unbox the value as `Self::Output`.
62    ///
63    /// Safety: The default implementation assumes that `Self::Output` is the correct layout for
64    /// the data that `value` points to.
65    #[inline]
66    unsafe fn unbox(value: Value) -> Self::Output {
67        unsafe { value.data_ptr().cast::<Self::Output>().read() }
68    }
69}
70
71macro_rules! impl_unboxer {
72    ($type:ty, $unboxer:expr_2021) => {
73        unsafe impl Unbox for $type {
74            type Output = Self;
75            #[inline]
76            unsafe fn unbox(value: Value) -> $type {
77                unsafe {
78                    $unboxer(
79                        <Value as crate::data::managed::private::ManagedPriv>::unwrap(
80                            value,
81                            $crate::private::Private,
82                        ),
83                    ) as _
84                }
85            }
86        }
87    };
88}
89
90impl_unboxer!(u8, jl_unbox_uint8);
91impl_unboxer!(u16, jl_unbox_uint16);
92impl_unboxer!(u32, jl_unbox_uint32);
93impl_unboxer!(u64, jl_unbox_uint64);
94impl_unboxer!(i8, jl_unbox_int8);
95impl_unboxer!(i16, jl_unbox_int16);
96impl_unboxer!(i32, jl_unbox_int32);
97impl_unboxer!(i64, jl_unbox_int64);
98impl_unboxer!(f32, jl_unbox_float32);
99impl_unboxer!(f64, jl_unbox_float64);
100impl_unboxer!(*mut c_void, jl_unbox_voidpointer);
101impl_unboxer!(usize, jlrs_unbox_ulong);
102impl_unboxer!(isize, jlrs_unbox_long);
103
104unsafe impl<T: IntoJulia> Unbox for *mut T {
105    type Output = Self;
106}