dtolnay / cxx

Safe interop between Rust and C++
https://cxx.rs
Apache License 2.0
5.87k stars 332 forks source link

Giving back ownership of an opaque type from C++ back to Rust #1384

Open acycliczebra opened 2 weeks ago

acycliczebra commented 2 weeks ago

Suppose that I create the following interface:


extern "Rust" {

    type Foo; // Some opaque type managed by rust

    fn create_foo() -> Box<Foo>; // C++ gets a reference to Foo

    fn do_something_with_foo(foo: Box<Foo>) -> Result<i32>; // C++ gives back ownership of Foo to rust at a later time
}

The above code compiles fine, but I'm not sure if it should. This is because the generated code looks like this:

::std::int32_t do_something_with_foo(::rust::Box<::systemscore::Foo> foo) {
  ::rust::MaybeUninit<::std::int32_t> return$;
  ::rust::repr::PtrLen error$ = systemscore$cxxbridge1$do_something_with_foo(foo.into_raw(), &return$.value);
  if (error$.ptr) {
    throw ::rust::impl<::rust::Error>::error(error$);
  }
  return ::std::move(return$.value);
}

The parameter signature doesn't make sense to me, because I would expect it to take foo by rvalue reference, that way I can call do_something_with_foo(std::move(boxed_foo)) and value gets moved into the function, and cleaned up by rust. As it stands now, I can never use this function, because foo can never be copied or created, so it can never be called. Is this expected behavior? If yes, then how would this be useful?

dtolnay commented 2 weeks ago

This is behaving correctly. rust::Box is move-constructible. So you can call do_something_with_foo(std::move(boxed_foo)).