Taaitaaiger / jlrs

Julia bindings for Rust
MIT License
429 stars 21 forks source link

Taking Rust-native types from Julia by value #120

Closed har7an closed 7 months ago

har7an commented 8 months ago

Hello,

thanks again for helping me out with #119! I've since poked around in the code I'm trying to wrapup/expose and found another thing I don't know how to work around. This time I want to create instances of Rust types in Julia (which I'm capable of now) and then mutate them. I'm treating all of the involved types as opaque, but my problem is I can't seem to find a way that allows passing in instances of other Rust types into the function and make them usable.

I have yet another example:

Rust Code Example

```rust use jlrs::{ data::{ managed::value::typed::{TypedValue, TypedValueRef, TypedValueRet}, types::foreign_type::OpaqueType, }, prelude::*, }; #[derive(Debug, Default)] pub enum FooTypeInner { Apples = 0, Bananas, Oranges, #[default] Raspberries, } #[derive(Debug, Default)] pub struct FooType { _inner: FooTypeInner, } unsafe impl OpaqueType for FooType {} #[derive(Debug, Default)] pub struct BarType { foo: FooType, } unsafe impl OpaqueType for BarType {} impl FooType { pub fn new_apples() -> TypedValueRet { unsafe { CCall::stackless_invoke(|unrooted| { TypedValue::new( unrooted, Self { _inner: FooTypeInner::Apples, }, ) .leak() }) } } pub fn is_apples(&self) -> u8 { matches!(self._inner, FooTypeInner::Apples).into() } } impl BarType { pub fn new() -> TypedValueRet { unsafe { CCall::stackless_invoke(|unrooted| TypedValue::new(unrooted, Default::default()).leak()) } } pub fn set_foo(&mut self, new_foo: TypedValue) { todo!("and this is where I'm stuck"); // self.foo = new_foo ... ? } } fn main() { println!("Hello, world!"); } jlrs::prelude::julia_module! { become example_bootstrap; struct FooType; in FooType fn new_apples() -> TypedValueRet as make_apples; in FooType fn is_apples(&self) -> u8 as is_apples; struct BarType; in BarType fn new() -> TypedValueRet as new_bar; in BarType fn set_foo(&mut self, new_foo: TypedValue) as set_foo!; fn main() -> () as say_hello; } ```

What I've figured out so far is that anything that is to be taken as argument in exposed functions must impl CCallArg. Given the list of rust types implementing that trait, TypedValue seems to be what I want to go with, especially since my "constructors" return a TypedValueRet. And this is where I'm stuck, shown in the todo! in the code above: Once I have that ValueRef I don't know what to do with it.

I have tried something like track_shared() and other functions that sounded plausible to me, but at some point I fail due to trait constraints on my custom types. For example, instaces of Tracked can only be derefed when their wrapped T implements ValidLayout, which, according to the docs, shouldn't be derived by hand is meant primarily for JlrsReflect. So how exactly do I go on here?

Thank you in advance!

Taaitaaiger commented 8 months ago

Foreign types that implement Clone can be unboxed, so if you derive Clone for FooTypeInner and FooType, the following should work:

self.foo = new_foo
    .as_value()
    .unbox::<FooType>()
    .expect("Not a FooType");
har7an commented 7 months ago

Alright, it works. Thanks for the tip!