Repository: dtolnay/cxx (language: rust)

Issue:
Pass Vec of FFI-defined shared struct from C++ to Rust?
I have a cxxbridge section that defines a shared type `Foo`, and I'd like to pass multiple such `Foo` objects from C++ to Rust in either a `std::vector` or a Rust `Vec`. The values need to be owned values, not references, because `Foo` in turn contains a `Box<RustOpaqueType>` that I need to move out of.

Is this possible today? `std::vector` can only be passed by reference, so my initial attempt using `CxxVector` failed. There also doesn't seem to be any way to build a Rust `Vec` from the C++ side.


Discussion / hints:
Code generation using cxxbridge-cmd succeeded for this workaround attempt but then failed in the Rust build. I have another `&mut Box<OpaqueType>` parameter elsewhere and that works fine, so I guess this is something specific to `Vec`?

```
#[cxx::bridge(namespace = process::ffi)]
mod ffi {
    struct StartupHandle {
        handle: Box<Handle>,
        handle_type: u16,
        arg: u16,
    }

    extern "Rust" {
        type Handle;

        fn add_handle_to_vec(vec: &mut Vec<StartupHandle>, handle: StartupHandle);
    }
}

fn add_handle_to_vec(vec: &mut Vec<ffi::StartupHandle>, handle: ffi::StartupHandle) {
    vec.push(handle);
}
```

```
error[E0308]: mismatched types
  --> ../../src/lib/process_builder/ffi/lib.rs:13:1
   |
13 | #[cxx::bridge(namespace = process::ffi)]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability
   |
   = note: expected mutable reference `&mut std::vec::Vec<ffi::StartupHandle>`
                      found reference `&std::vec::Vec<ffi::StartupHandle>`
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
```
Here's the section of expanded macro (from `cargo expand`) that it seems to be failing on, note the non-mutable `::cxx::private::RustVec` reference:

```
        #[doc(hidden)]
        #[export_name = "process$ffi$cxxbridge03$add_handle_to_vec"]
        unsafe extern "C" fn __add_handle_to_vec(
            vec: &::cxx::private::RustVec<StartupHandle>,
            handle: *mut StartupHandle,
        ) {
            let __fn = "_ffi_rust_lib_rustc_static::ffi::add_handle_to_vec";
            ::cxx::private::catch_unwind(__fn, move || {
                super::add_handle_to_vec(vec.as_vec(), ::std::ptr::read(handle))
            })
        }
```
> ```console
>    = note: expected mutable reference `&mut std::vec::Vec<ffi::StartupHandle>`
>                       found reference `&std::vec::Vec<ffi::StartupHandle>`
> ```

The fact that we generate noncompilable code here is a bug. What you wrote is supposed to work.

<br>

> `std::vector` can only be passed by reference, so my initial attempt using `CxxVector` failed.

Yeah, std::vector has a move constructor so Rust-style byte copy moves would screw it up. Therefore we can't allow std::vector to exist by value on the Rust side. As a workaround that isn't incompatible with Rust you can use std::unique_ptr\<std::vector\<T\>\> which is supported today ([tested here](https://github.com/dtolnay/cxx/blob/85487b00f8790fe28e8d82e9dfab3b3145503631/tests/ffi/lib.rs#L40)).
I'm happy to take a swing at fixing the bug, I'll see if I can find some time over the next couple days.

Fix patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 431fd3ba7..ab1206c55 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -256,10 +256,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
             Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    quote!(::cxx::private::RustString::from_ref(#var))
-                }
-                Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)),
+                Type::Ident(ident) if ident == RustString => match ty.mutability {
+                    None => quote!(::cxx::private::RustString::from_ref(#var)),
+                    Some(_) => quote!(::cxx::private::RustString::from_mut(#var)),
+                },
+                Type::RustVec(_) => match ty.mutability {
+                    None => quote!(::cxx::private::RustVec::from_ref(#var)),
+                    Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)),
+                },
                 _ => quote!(#var),
             },
             Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
@@ -331,10 +335,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
             Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))),
             Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    Some(quote!(#call.map(|r| r.as_string())))
-                }
-                Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))),
+                Type::Ident(ident) if ident == RustString => match ty.mutability {
+                    None => Some(quote!(#call.map(|r| r.as_string()))),
+                    Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))),
+                },
+                Type::RustVec(_) => match ty.mutability {
+                    None => Some(quote!(#call.map(|r| r.as_vec()))),
+                    Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec()))),
+                },
                 _ => None,
             },
             Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
@@ -348,8 +356,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
             Type::RustVec(_) => Some(quote!(#call.into_vec())),
             Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
-                Type::RustVec(_) => Some(quote!(#call.as_vec())),
+                Type::Ident(ident) if ident == RustString => match ty.mutability {
+                    None => Some(quote!(#call.as_string())),
+                    Some(_) => Some(quote!(#call.as_mut_string())),
+                },
+                Type::RustVec(_) => match ty.mutability {
+                    None => Some(quote!(#call.as_vec())),
+                    Some(_) => Some(quote!(#call.as_mut_vec())),
+                },
                 _ => None,
             },
             Type::Str(_) => Some(quote!(#call.as_str())),
@@ -497,8 +511,14 @@ fn expand_rust_function_shim_impl(
             Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())),
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
-                Type::RustVec(_) => quote!(#ident.as_vec()),
+                Type::Ident(i) if i == RustString => match ty.mutability {
+                    None => quote!(#ident.as_string()),
+                    Some(_) => quote!(#ident.as_mut_string()),
+                },
+                Type::RustVec(_) => match ty.mutability {
+                    None => quote!(#ident.as_vec()),
+                    Some(_) => quote!(#ident.as_mut_vec()),
+                },
                 _ => quote!(#ident),
             },
             Type::Str(_) => quote!(#ident.as_str()),
@@ -532,10 +552,14 @@ fn expand_rust_function_shim_impl(
             Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))),
             Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    Some(quote!(::cxx::private::RustString::from_ref(#call)))
-                }
-                Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))),
+                Type::Ident(ident) if ident == RustString => match ty.mutability {
+                    None => Some(quote!(::cxx::private::RustString::from_ref(#call))),
+                    Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))),
+                },
+                Type::RustVec(_) => match ty.mutability {
+                    None => Some(quote!(::cxx::private::RustVec::from_ref(#call))),
+                    Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))),
+                },
                 _ => None,
             },
             Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
@@ -860,14 +884,19 @@ fn expand_extern_type(ty: &Type) -> TokenStream {
             let elem = expand_extern_type(&ty.inner);
             quote!(::cxx::private::RustVec<#elem>)
         }
-        Type::Ref(ty) => match &ty.inner {
-            Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
-            Type::RustVec(ty) => {
-                let inner = expand_extern_type(&ty.inner);
-                quote!(&::cxx::private::RustVec<#inner>)
+        Type::Ref(ty) => {
+            let mutability = ty.mutability;
+            match &ty.inner {
+                Type::Ident(ident) if ident == RustString => {
+                    quote!(&#mutability ::cxx::private::RustString)
+                }
+                Type::RustVec(ty) => {
+                    let inner = expand_extern_type(&ty.inner);
+                    quote!(&#mutability ::cxx::private::RustVec<#inner>)
+                }
+                _ => quote!(#ty),
             }
-            _ => quote!(#ty),
-        },
+        }
         Type::Str(_) => quote!(::cxx::private::RustStr),
         Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
         _ => quote!(#ty),
diff --git a/src/rust_string.rs b/src/rust_string.rs
index a923ced92..f0d1df19b 100644
--- a/src/rust_string.rs
+++ b/src/rust_string.rs
@@ -14,6 +14,10 @@ impl RustString {
         unsafe { &*(s as *const String as *const RustString) }
     }
 
+    pub fn from_mut(s: &mut String) -> &mut Self {
+        unsafe { &mut *(s as *mut String as *mut RustString) }
+    }
+
     pub fn into_string(self) -> String {
         self.repr
     }
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
index 4c5035d66..9ff4bbf20 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -16,6 +16,10 @@ impl<T> RustVec<T> {
         unsafe { &*(v as *const Vec<T> as *const RustVec<T>) }
     }
 
+    pub fn from_mut(v: &mut Vec<T>) -> &mut Self {
+        unsafe { &mut *(v as *mut Vec<T> as *mut RustVec<T>) }
+    }
+
     pub fn into_vec(self) -> Vec<T> {
         self.repr
     }


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index e49134308..d69163acf 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -32,6 +32,7 @@ pub mod ffi {
         fn c_return_box() -> Box<R>;
         fn c_return_unique_ptr() -> UniquePtr<C>;
         fn c_return_ref(shared: &Shared) -> &usize;
+        fn c_return_mut(shared: &mut Shared) -> &mut usize;
         fn c_return_str(shared: &Shared) -> &str;
         fn c_return_sliceu8(shared: &Shared) -> &[u8];
         fn c_return_rust_string() -> String;
@@ -41,8 +42,10 @@ pub mod ffi {
         fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
         fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
         fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
+        fn c_return_mut_vector(c: &mut C) -> &mut CxxVector<u8>;
         fn c_return_rust_vec() -> Vec<u8>;
         fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
+        fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec<u8>;
         fn c_return_identity(_: usize) -> usize;
         fn c_return_sum(_: usize, _: usize) -> usize;
         fn c_return_enum(n: u16) -> Enum;
@@ -113,11 +116,13 @@ pub mod ffi {
         fn r_return_box() -> Box<R>;
         fn r_return_unique_ptr() -> UniquePtr<C>;
         fn r_return_ref(shared: &Shared) -> &usize;
+        fn r_return_mut(shared: &mut Shared) -> &mut usize;
         fn r_return_str(shared: &Shared) -> &str;
         fn r_return_rust_string() -> String;
         fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
         fn r_return_rust_vec() -> Vec<u8>;
         fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
+        fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
         fn r_return_identity(_: usize) -> usize;
         fn r_return_sum(_: usize, _: usize) -> usize;
         fn r_return_enum(n: u32) -> Enum;
@@ -195,6 +200,10 @@ fn r_return_ref(shared: &ffi::Shared) -> &usize {
     &shared.z
 }
 
+fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
+    &mut shared.z
+}
+
 fn r_return_str(shared: &ffi::Shared) -> &str {
     let _ = shared;
     "2020"
@@ -220,6 +229,11 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
     unimplemented!()
 }
 
+fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec<u8> {
+    let _ = shared;
+    unimplemented!()
+}
+
 fn r_return_identity(n: usize) -> usize {
     n
 }
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 4cc771e58..b21393010 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -35,6 +35,8 @@ size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
 
 const std::vector<uint8_t> &C::get_v() const { return this->v; }
 
+std::vector<uint8_t> &C::get_v() { return this->v; }
+
 size_t c_return_primitive() { return 2020; }
 
 Shared c_return_shared() { return Shared{2020}; }
@@ -49,6 +51,8 @@ std::unique_ptr<C> c_return_unique_ptr() {
 
 const size_t &c_return_ref(const Shared &shared) { return shared.z; }
 
+size_t &c_return_mut(Shared &shared) { return shared.z; }
+
 rust::Str c_return_str(const Shared &shared) {
   (void)shared;
   return "2020";
@@ -99,6 +103,8 @@ const std::vector<uint8_t> &c_return_ref_vector(const C &c) {
   return c.get_v();
 }
 
+std::vector<uint8_t> &c_return_mut_vector(C &c) { return c.get_v(); }
+
 rust::Vec<uint8_t> c_return_rust_vec() {
   throw std::runtime_error("unimplemented");
 }
@@ -108,6 +114,11 @@ const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c) {
   throw std::runtime_error("unimplemented");
 }
 
+rust::Vec<uint8_t> &c_return_mut_rust_vec(C &c) {
+  (void)c;
+  throw std::runtime_error("unimplemented");
+}
+
 size_t c_return_identity(size_t n) { return n; }
 
 size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; }
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 7b7d273e4..fe644ebd1 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -19,6 +19,7 @@ class C {
   size_t set_succeed(size_t n);
   size_t get_fail();
   const std::vector<uint8_t> &get_v() const;
+  std::vector<uint8_t> &get_v();
 
 private:
   size_t n;
@@ -35,6 +36,7 @@ Shared c_return_shared();
 rust::Box<R> c_return_box();
 std::unique_ptr<C> c_return_unique_ptr();
 const size_t &c_return_ref(const Shared &shared);
+size_t &c_return_mut(Shared &shared);
 rust::Str c_return_str(const Shared &shared);
 rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared);
 rust::String c_return_rust_string();
@@ -44,8 +46,10 @@ std::unique_ptr<std::vector<double>> c_return_unique_ptr_vector_f64();
 std::unique_ptr<std::vector<Shared>> c_return_unique_ptr_vector_shared();
 std::unique_ptr<std::vector<C>> c_return_unique_ptr_vector_opaque();
 const std::vector<uint8_t> &c_return_ref_vector(const C &c);
+std::vector<uint8_t> &c_return_mut_vector(C &c);
 rust::Vec<uint8_t> c_return_rust_vec();
 const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c);
+rust::Vec<uint8_t> &c_return_mut_rust_vec(C &c);
 size_t c_return_identity(size_t n);
 size_t c_return_sum(size_t n1, size_t n2);
 Enum c_return_enum(uint16_t n);