extendr: robj::try_into() fails for Result> where MyStruct derived with #[extendr]

Dear extendries

I get an error which comes from this check

test tests::it_works ... FAILED

failures:

---- tests::it_works stdout ----
[src/lib.rs:38] &robj = ExternalPtr.set_class(["MyStruct"]
thread 'tests::it_works' panicked at 'called `Result::unwrap()` on an `Err` value: ExpectedExternalPtrType(ExternalPtr.set_class(["MyStruct"], "helloextendr::MyStruct")', src/lib.rs:16:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I try to convert Robj to MyStruct but it seems to fail because ptr tag is ‘MyStruct’ and typename is “helloextendr::MyStruct” robj::try_into() fails for Result<ExternalPtr<MyStruct>>.

the unit test only covers a trivial type not a user-defined #[extendr]-exported struct

all the best

lib.rs tests.R

use extendr_api::prelude::*;

#[derive(Debug, Clone)]
#[extendr]
struct MyStruct(String); //could be any type

#[extendr]
impl MyStruct {
    pub fn new() -> Self {
        MyStruct("applepie".into())
    }

    //restore_from_robj must take Robj, not MyStruct as input
    pub fn restore_from_robj(robj: Robj) -> Self {
        let res: Result<ExternalPtr<MyStruct>> = robj.try_into(); // this fails
        let x = res.unwrap().0.clone();
        MyStruct(x)
    }
}

// Macro to generate exports.
extendr_module! {
    mod helloextendr;
    impl MyStruct;
}

#[cfg(test)]
mod tests {
    use super::*;
    use extendr_api::prelude::extendr_engine::{end_r, start_r};

    #[test]

    //build and install package to access it from rust side
    fn it_works() {
        start_r();
        let robj = R!("helloextendr:::MyStruct$new()").unwrap();
        dbg!(&robj);
        let mystruct = MyStruct::restore_from_robj(robj);
        assert_eq!(mystruct.0, "applpie".to_string());
        end_r();
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 22 (22 by maintainers)

Commits related to this issue

Most upvoted comments

cargo tests were alright, just needed to build package fully and not use rextendr::document().

@Ilia-Kosenkov

If you can think of another test case worth adding to the repo, let me know I can rewrite the two other cargo tests into R tests as a fork of your PR next week.

Tags now are created with the correct type name. It is strange as I ran your test project against my branch and it worked. I also added a test to re-create your scenario. Can you check the PR?

I see your point. The problem here is that we are providing an API to others and thus we have to be very precise about type conversion. We might consider two things here:

  • indeed provide an unsafe function that will behave as, basically, reinterpret_cast<>() or something similar – take the pointer and cast it to whatever you belive is correct, no guarantees;

  • update current implementation to provide partial safety – we can notify the user whether we were able to match the type

    • fully (incl namespaces/crates)
    • partially (we matched struct name but not the full qualification, your case), so treat it carefully
    • no match – equivalent to Err() case in the current implementation
  • improve tag generation by embedding more metadata – perhaps information about the type shape & size. That way we can allow reinterpreting the pointer to any type of the same shape and size

I do not have a lot of free time right now, but this thing is top-1 priority for me. Let me do some more research and I’ll get back to you.