d-unsed / ruru

Native Ruby extensions written in Rust
MIT License
832 stars 40 forks source link

How exactly do you use `protect` ? #85

Closed danielpclark closed 6 years ago

danielpclark commented 6 years ago

I've been trying to guess at how to use it but I haven't been able to get anything working. I was able to implement a form of eval_protect but that was entirely different from this. Could you show me an example? Thanks!

danielpclark commented 6 years ago

Looking at the PR https://github.com/tildeio/helix/pull/64 it seems that in order for me to use protect I need to pass it an extern "C" fn in order for it to work. He has two macros doing this in the PR ruby_safe_f and ruby_funcall.

What I'm looking to do is have an implementation of the send method you have to operate with Ruby's rb_protect. I'd like to name it protect_send and protect_public_send. This way we won't ever crash our program from exceptions being raised from Ruby. Our return type can be Result<T, AnyObject> or Result<AnyObject, AnyObject>. Look at my eval implementation to see what I mean.

danielpclark commented 6 years ago

I believe I've discovered the issue. Some of the updates done to make the thread code work also needs to be done to protect. I'm looking at how to fix it.

The first thing to note is the type for F needs to change from

    pub fn protect<F>(func: F) -> Result<Value, i32>
    where
        F: FnOnce(),
    {   
        vm::protect(func)
    }   

to

    pub fn protect<F>(func: F) -> Result<Value, i32>
    where
        F: 'static + FnOnce() -> Value,
    {   
        vm::protect(func)
    }   

Just like what was done for the thread methods. This allows closures to be passed in.


Then a new problem arises. The Value now returned from protect is wrong.

Format is Ruby => ValueType

I suspect it may have to do with Value going through the C API to Rust twice.


And now a segfault. Yeah, rb_protect needs to be rewritten. The Helix PR should be helpful: https://github.com/tildeio/helix/pull/64

danielpclark commented 6 years ago

I figured it out. It was rather simple in the end. Even if I did spend two days learning a lot in the process of trying to get it to work.

Here's the diff that's needed:

-pub fn protect<F>(func: F) -> Result<Value, c_int>
+pub fn protect<F, R>(func: F) -> Result<Value, c_int>
 where
-    F: FnOnce(),
+    F: FnOnce() -> R,

I'll include it in my protect_send PR.