With restrictions

In the previous section we've seen that we can derive OpaqueType for types with generics. There's a limitation, though: if a generic of the type is bounded by a trait which isn't implemented for (), deriving the trait will fail.

Every opaque type has an associated Key type, which must be unique and the same for the entire family of types (i.e. it must not depend on any of the generics). It's normally generated by replacing all generics with (). We have to provide a custom Key if this default type is rejected due to bounds on the type, it can be set with the #[jlrs(key = "path::to::Type")] attribute. The key type must implement Any. In practice, it's best to use one of the exported variants or a custom zero-sized type.

use std::fmt::Debug;

use jlrs::{
    data::{
        managed::{ccall_ref::CCallRefRet, value::typed::TypedValue},
        types::construct_type::ConstructType,
    },
    prelude::*,
    weak_handle,
};

pub trait IsFloat {}
impl IsFloat for f32 {}
impl IsFloat for f64 {}

#[derive(Debug, OpaqueType)]
#[jlrs(key = "Opaque<f64>")]
struct Opaque<T: IsFloat> {
    _a: T,
}

impl<T: 'static + Send + Sync + ConstructType + Debug + IsFloat> Opaque<T> {
    fn new(a: T) -> CCallRefRet<Opaque<T>> {
        match weak_handle!() {
            Ok(handle) => CCallRefRet::new(TypedValue::new(handle, Opaque { _a: a }).leak()),
            Err(_) => panic!("not called from Julia"),
        }
    }

    fn print(&self) {
        println!("{:?}", self)
    }
}

julia_module! {
    become julia_module_tutorial_init_fn;

    for T in [f32, f64] {
        struct Opaque<T>;
        in Opaque<T> fn new(a: T) -> CCallRefRet<Opaque<T>> as Opaque;
        in Opaque<T> fn print(&self);
    };

    type OpaqueF32 = Opaque<f32>;
    in Opaque<f32> fn new(a: f32) -> CCallRefRet<Opaque<f32>> as OpaqueF32;
}
julia> module JuliaModuleTutorial ... end
Main.JuliaModuleTutorial

julia> v =  JuliaModuleTutorial.Opaque(Float32(3.0))
Main.JuliaModuleTutorial.Opaque{Float32}()

julia> v =  JuliaModuleTutorial.Opaque(Float64(3.0))
Main.JuliaModuleTutorial.Opaque{Float64}()

julia> v =  JuliaModuleTutorial.OpaqueF32(Float32(3.0))
Main.JuliaModuleTutorial.Opaque{Float32}()

julia> JuliaModuleTutorial.print(v)
Opaque { _a: 3.0 }