Repository: dtolnay/cxx (language: rust)

Issue:
How do you clear a Vec<T> from C++?
I have a shared struct that contains a Rust Vec.

``` Rust
struct Foo {
    values: Vec<f32>
}
```

This becomes a `rust::cxxbridge::Vec` on the C++ side.
Unfortunately this class doesn't seem to have a `clear` method which means I cannot re-use the same vector.

How can I clear an existing `Vec` from C++?



Discussion / hints:
```rust
#[cxx::bridge]
mod ffi {
    extern "Rust" {
        fn clear_vec(v: &mut Vec<f32>);
    }
}

fn clear_vec(v: &mut Vec<f32>) {
    v.clear();
}
```

This is callable from C++ as `clear_vec(foo.values)`.

Separately, we could build a `clear` member function into rust::Vec if somebody sends a PR.

Fix patch:
diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md
index 948fa6713..befd6fbb4 100644
--- a/book/src/binding/vec.md
+++ b/book/src/binding/vec.md
@@ -45,6 +45,7 @@ public:
   void reserve(size_t new_cap);
   void push_back(const T &value);
   void push_back(T &&value);
+  void clear();
   template <typename... Args>
   void emplace_back(Args &&...args);
 
diff --git a/gen/src/write.rs b/gen/src/write.rs
index a3b9c9fa9..f02d5847a 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1475,6 +1475,11 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
         "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
         instance, inner,
     );
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$clear(::rust::Vec<{}> *ptr) noexcept;",
+        instance, inner,
+    );
 }
 
 fn write_rust_box_impl(out: &mut OutFile, key: NamedImplKey) {
@@ -1585,6 +1590,19 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
         instance,
     );
     writeln!(out, "}}");
+    writeln!(out, "template <>");
+    begin_function_definition(out);
+    writeln!(
+        out,
+        "void Vec<{}>::clear() noexcept {{",
+        inner,
+    );
+    writeln!(
+        out,
+        "  return cxxbridge1$rust_vec${}$clear(this);",
+        instance,
+    );
+    writeln!(out, "}}");
 }
 
 fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
diff --git a/include/cxx.h b/include/cxx.h
index dffcb01ab..f8ccb6f6c 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -318,6 +318,7 @@ class Vec final {
   T &at(std::size_t n);
   T &front() noexcept;
   T &back() noexcept;
+  void clear() noexcept;
 
   void reserve(std::size_t new_cap);
   void push_back(const T &value);
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 5a879cbde..60a2affad 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1255,6 +1255,7 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
     let link_data = format!("{}data", link_prefix);
     let link_reserve_total = format!("{}reserve_total", link_prefix);
     let link_set_len = format!("{}set_len", link_prefix);
+    let link_clear = format!("{}clear", link_prefix);
 
     let local_prefix = format_ident!("{}__vec_", elem);
     let local_new = format_ident!("{}new", local_prefix);
@@ -1264,6 +1265,7 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
     let local_data = format_ident!("{}data", local_prefix);
     let local_reserve_total = format_ident!("{}reserve_total", local_prefix);
     let local_set_len = format_ident!("{}set_len", local_prefix);
+    let local_clear = format_ident!("{}clear", local_prefix);
 
     let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
 
@@ -1309,6 +1311,11 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
         unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) {
             (*this).set_len(len);
         }
+        #[doc(hidden)]
+        #[export_name = #link_clear]
+        unsafe extern "C" fn #local_clear #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
+            (*this).clear();
+        }
     }
 }
 
diff --git a/src/cxx.cc b/src/cxx.cc
index 5dca531be..03f9c5af2 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -567,6 +567,8 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
       const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
   void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(                        \
       rust::Vec<CXX_TYPE> *ptr, std::size_t new_cap) noexcept;                 \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$clear(                                \
+      rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
   void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr,     \
                                                  std::size_t len) noexcept;
 
@@ -598,7 +600,11 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
   template <>                                                                  \
   void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept {                      \
     cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len);                      \
-  }
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::clear() noexcept {                                       \
+    cxxbridge1$rust_vec$##RUST_TYPE##$clear(this);                             \
+  }   
 
 #define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE)                                    \
   static_assert(sizeof(std::shared_ptr<CXX_TYPE>) == 2 * sizeof(void *), "");  \
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
index 9f4484dba..c70d8d235 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -67,6 +67,10 @@ impl<T> RustVec<T> {
     pub unsafe fn set_len(&mut self, len: usize) {
         unsafe { self.as_mut_vec().set_len(len) }
     }
+
+    pub fn clear(&mut self) {
+        self.as_mut_vec().clear()
+    }
 }
 
 impl RustVec<RustString> {
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index 6f2dab9d3..a4c2c5f7d 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -54,6 +54,12 @@ macro_rules! rust_vec_shims {
                     unsafe { (*this).set_len(len) }
                 }
             }
+            attr! {
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$clear")]
+                unsafe extern "C" fn __clear(this: *mut RustVec<$ty>) {
+                    unsafe { (*this).clear() }
+                }
+            }
         };
     };
 }


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 25b4ccdc8..8981178bb 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -149,6 +149,7 @@ pub mod ffi {
         fn c_take_rust_vec_index(v: Vec<u8>);
         fn c_take_rust_vec_shared_index(v: Vec<Shared>);
         fn c_take_rust_vec_shared_push(v: Vec<Shared>);
+        fn c_take_rust_vec_shared_clear(v: Vec<Shared>);
         fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
         fn c_take_rust_vec_shared_sort(v: Vec<Shared>);
         fn c_take_ref_rust_vec(v: &Vec<u8>);
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index df7ded07f..f85d2ec22 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -452,6 +452,13 @@ void c_take_rust_vec_shared_push(rust::Vec<Shared> v) {
   }
 }
 
+void c_take_rust_vec_shared_clear(rust::Vec<Shared> v) {
+  v.clear();
+  if (v.size() == 0) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_ref_rust_vec(const rust::Vec<uint8_t> &v) {
   uint8_t sum = std::accumulate(v.begin(), v.end(), 0);
   if (sum == 200) {
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index b624d3933..52d853a09 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -152,6 +152,7 @@ void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v);
 void c_take_rust_vec_string(rust::Vec<rust::String> v);
 void c_take_rust_vec_shared_index(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_push(rust::Vec<Shared> v);
+void c_take_rust_vec_shared_clear(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_sort(rust::Vec<Shared> v);
 void c_take_ref_rust_vec(const rust::Vec<uint8_t> &v);
diff --git a/tests/test.rs b/tests/test.rs
index 1f0b16603..d536a359b 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -167,6 +167,7 @@ fn test_c_take() {
     check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_clear(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_forward_iterator(
         shared_test_vec,
     ));