Delegate Rust object to GC language


#1

I’m currently writing a wrapper for mruby and things went pretty well until now.

Basically all Rust objects (structs or enums) are sent to mruby and wrapped in mruby objects. The way I’m doing this is I’m sending Rcs to mruby by transmuting them to void pointers. What I’m trying to do is just keep a reference to how many strong rerefences I have to that Rust object, and Rc seems to be great for the task. mruby has a free callback where I’m getting back the objects. In that callback I’m reclaiming the Rc back to Rust with transmute.

The problem arises when I initialize one of these objects from mruby and call a method on it.

let mruby = MRuby::new();

struct Cont {
    value: i32
}

mruby.def_class::<Cont>("Container");
mruby.def_method("Container", "initialize", mrfn!(|mruby, slf, v: i32| {
    let cont = Cont { value: v };

    slf.init("Container", cont)
}));

mruby.def_method("Container", "value", mrfn!(|mruby, slf| {
    let cont = slf.to_obj::<Cont>("Container").unwrap();

    mruby.fixnum(cont.value)
}));

let obj = mruby.run("Container.new(3)").unwrap();
let val = obj.call("value", vec![]);

This gets panicked at 'arithmetic operation overflowed', src/liballoc/rc.rs:866 because the number of strong references (which is 0; I don’t really see why) gets decremented.

I can’t really see what I’m doing wrong. Can anyone see what I’m doing wrong? Is there any better way of doing this?


#2

Not sure about this particular error but transmuting Rc<T> like that seems very risky. Do you ensure safe code can’t get Rc<U> fed to free<T>?

use std::mem;
use std::rc::Rc;

fn main(){
	let rc = Rc::new((1111111111111111111u64, 1111111111111111111u64));
	unsafe {
	    mem::transmute::<_, Rc<String>>(rc); // boom!
	};
}

#3

By using def_class<T>, you can only define free<T> for that particular mruby class. So I’m pretty sure that’s not the problem.

The error I get is caused before any free<T> gets the chance to be called by mruby, anyway.


#4

[quote=“dragostis, post:3, topic:4723”]
By using def_class<T>, you can only define free<T> for that particular mruby class.
[/quote]I don’t see anything tying T to name there.


#5

Not to name, but to T. Of course, if you mix name up with a wrong T, it will wreak havoc. I don’t think you can link them somehow. I need to use name in order to get the closures from MRuby's HashMap and I can’t use T as a key to it.


#6

[quote=“dragostis, post:5, topic:4723”]
Of course, if you mix name up with a wrong T, it will wreak havoc.
[/quote]An API with such properties must be marked unsafe.


#7

I think I can fix this by wrapping those calls in macros.

macro_rules! def {
    ( $name:tt ) => ( def_class<$name>(stringify!($name)) );
}

#8

You might want to look at what std::any has to offer. By the way I have a hunch that T there also needs a 'static bound.


#9

I don’t really see how I could use Any here. I could build some pattern matchers with all the Rust types that I want to use and then use Anys but that seems overly complicated. But then again, I probably can’t see what you’re aiming at.

I don’t see why the Rust types that I’m sending to mruby should be 'static either.


#10

[quote=“dragostis, post:9, topic:4723”]
I don’t really see how I could use Any here. I could build some pattern matchers with all the Rust types that I want to use and then use Anys but that seems overly complicated. But then again, I probably can’t see what you’re aiming at.
[/quote]An immediate application is you can get rid of name and index the classes map by TypeId.

[quote=“dragostis, post:9, topic:4723”]
I don’t see why the Rust types that I’m sending to mruby should be 'static either.
[/quote]Shared ownership (e.g. Rc) often implies you don’t know in any one place how long the value is going to live. But you can’t allow the Rc to outlive any references T could have borrowed. A conservative solution is requiring T: 'static i.e. not hold any non-static references.


#11

This sounds really good.

How can it outlive? Any Rc sent to mruby gives its full ownership.


#12

Still, def_class<T> does not get any object that I can get a TypeId from.


#13

TypeId::of will do. :smiley:


#14

Took a bit to wrap my head around this. Yes, it makes sense for T to be 'static. Still, this doesn’t solve the issue. :confused:


#15

@pnkfelix has been doing a lot of work on how Rust can integrate with external garbage collectors, regardless of what’s done in the meantime, this should help a LOT in the future.


#16

Hm, I think an example to play around with would help, preferably with a sys-crate that builds mruby directly.

I tried getting mrusty set up, but couldn’t do it on short notice, as I’m missing mruby.


#17

Well, I’ve fixed the issue in the meantime. I forgot to mem::forget a transmuted Rc. I’m implementing the Any design right now.

I’m not yet done with some of the basics. I still have a few important things to solve and I’ll update the README.md on how to build mruby. I’m not sure we can do an automatic build though, since mruby requires Ruby to compile (it relies on cross-compilation for embedded functionality). It’s probably not so complicated to do a build script for mruby using gcc, though.