Repository: dtolnay/cxx (language: rust)

Issue:
CxxVector::pop
We should expose functionality analogous to https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop on the Rust `CxxVector`.

This issues is split out from #778.


Discussion / hints:

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index f9627d36c..9811ff49f 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1833,6 +1833,7 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
     );
     writeln!(out, "  return s.size();");
     writeln!(out, "}}");
+
     writeln!(
         out,
         "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{",
@@ -1840,6 +1841,7 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
     );
     writeln!(out, "  return &(*s)[pos];");
     writeln!(out, "}}");
+
     if out.types.is_maybe_trivial(element) {
         writeln!(
             out,
@@ -1849,6 +1851,15 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
         writeln!(out, "  v->push_back(::std::move(*value));");
         writeln!(out, "  ::rust::destroy(value);");
         writeln!(out, "}}");
+
+        writeln!(
+            out,
+            "void cxxbridge1$std$vector${}$pop_back(::std::vector<{}> *v, {} *out) noexcept {{",
+            instance, inner, inner,
+        );
+        writeln!(out, "  ::new (out) {}(::std::move(v->back()));", inner);
+        writeln!(out, "  v->pop_back();");
+        writeln!(out, "}}");
     }
 
     out.include.memory = true;
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 5ff5f8ef6..8618555c9 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1538,6 +1538,7 @@ fn expand_cxx_vector(
     let link_size = format!("{}size", prefix);
     let link_get_unchecked = format!("{}get_unchecked", prefix);
     let link_push_back = format!("{}push_back", prefix);
+    let link_pop_back = format!("{}pop_back", prefix);
     let unique_ptr_prefix = format!(
         "cxxbridge1$unique_ptr$std$vector${}$",
         resolve.name.to_symbol(),
@@ -1555,7 +1556,7 @@ fn expand_cxx_vector(
     let unsafe_token = format_ident!("unsafe", span = begin_span);
 
     let can_pass_element_by_value = types.is_maybe_trivial(elem);
-    let push_back_method = if can_pass_element_by_value {
+    let by_value_methods = if can_pass_element_by_value {
         Some(quote_spanned! {end_span=>
             #[doc(hidden)]
             unsafe fn __push_back(
@@ -1571,6 +1572,20 @@ fn expand_cxx_vector(
                 }
                 __push_back(this, value);
             }
+            #[doc(hidden)]
+            unsafe fn __pop_back(
+                this: ::std::pin::Pin<&mut ::cxx::CxxVector<Self>>,
+                out: &mut ::std::mem::MaybeUninit<Self>,
+            ) {
+                extern "C" {
+                    #[link_name = #link_pop_back]
+                    fn __pop_back #impl_generics(
+                        this: ::std::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>,
+                        out: &mut ::std::mem::MaybeUninit<#elem #ty_generics>,
+                    );
+                }
+                __pop_back(this, out);
+            }
         })
     } else {
         None
@@ -1598,7 +1613,7 @@ fn expand_cxx_vector(
                 }
                 __get_unchecked(v, pos)
             }
-            #push_back_method
+            #by_value_methods
             #[doc(hidden)]
             fn __unique_ptr_null() -> *mut ::std::ffi::c_void {
                 extern "C" {
diff --git a/src/cxx.cc b/src/cxx.cc
index f1b8ad819..b9168a871 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -503,6 +503,11 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
       std::vector<CXX_TYPE> *v, CXX_TYPE *value) noexcept {                    \
     v->push_back(std::move(*value));                                           \
     destroy(value);                                                            \
+  }                                                                            \
+  void cxxbridge1$std$vector$##RUST_TYPE##$pop_back(std::vector<CXX_TYPE> *v,  \
+                                                    CXX_TYPE *out) noexcept {  \
+    new (out) CXX_TYPE(std::move(v->back()));                                  \
+    v->pop_back();                                                             \
   }
 
 #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE)                                  \
diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs
index 23f7621df..cc7a77734 100644
--- a/src/cxx_vector.rs
+++ b/src/cxx_vector.rs
@@ -8,7 +8,7 @@ use core::ffi::c_void;
 use core::fmt::{self, Debug};
 use core::iter::FusedIterator;
 use core::marker::{PhantomData, PhantomPinned};
-use core::mem::{self, ManuallyDrop};
+use core::mem::{self, ManuallyDrop, MaybeUninit};
 use core::pin::Pin;
 use core::ptr;
 use core::slice;
@@ -162,6 +162,21 @@ where
             T::__push_back(self, &mut value);
         }
     }
+
+    pub fn pop(self: Pin<&mut Self>) -> Option<T>
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        if self.is_empty() {
+            None
+        } else {
+            let mut value = MaybeUninit::uninit();
+            Some(unsafe {
+                T::__pop_back(self, &mut value);
+                value.assume_init()
+            })
+        }
+    }
 }
 
 /// Iterator over elements of a `CxxVector` by shared reference.
@@ -320,6 +335,14 @@ pub unsafe trait VectorElement: Sized {
         unreachable!()
     }
     #[doc(hidden)]
+    unsafe fn __pop_back(v: Pin<&mut CxxVector<Self>>, out: &mut MaybeUninit<Self>) {
+        // Opaque C type vector elements do not get this method because they can
+        // never exist by value on the Rust side of the bridge.
+        let _ = v;
+        let _ = out;
+        unreachable!()
+    }
+    #[doc(hidden)]
     fn __unique_ptr_null() -> *mut c_void;
     #[doc(hidden)]
     unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void;
@@ -331,7 +354,7 @@ pub unsafe trait VectorElement: Sized {
     unsafe fn __unique_ptr_drop(repr: *mut c_void);
 }
 
-macro_rules! vector_element_push_back {
+macro_rules! vector_element_by_value_methods {
     (opaque, $segment:expr, $ty:ty) => {};
     (trivial, $segment:expr, $ty:ty) => {
         #[doc(hidden)]
@@ -344,6 +367,16 @@ macro_rules! vector_element_push_back {
             }
             __push_back(v, value);
         }
+        #[doc(hidden)]
+        unsafe fn __pop_back(v: Pin<&mut CxxVector<$ty>>, out: &mut MaybeUninit<$ty>) {
+            extern "C" {
+                attr! {
+                    #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$pop_back")]
+                    fn __pop_back(_: Pin<&mut CxxVector<$ty>>, _: &mut MaybeUninit<$ty>);
+                }
+            }
+            __pop_back(v, out);
+        }
     };
 }
 
@@ -376,7 +409,7 @@ macro_rules! impl_vector_element {
                 }
                 __get_unchecked(v, pos)
             }
-            vector_element_push_back!($kind, $segment, $ty);
+            vector_element_by_value_methods!($kind, $segment, $ty);
             #[doc(hidden)]
             fn __unique_ptr_null() -> *mut c_void {
                 extern "C" {


Test patch:
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 98069b287..ff215ca00 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -336,7 +336,7 @@ void c_take_unique_ptr_string(std::unique_ptr<std::string> s) {
 }
 
 void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v) {
-  if (v->size() == 4) {
+  if (v->size() == 3) {
     cxx_test_suite_set_correct();
   }
 }
diff --git a/tests/test.rs b/tests/test.rs
index 60e943f19..1f0b16603 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -150,9 +150,9 @@ fn test_c_take() {
     check!(ffi::c_take_unique_ptr_string(
         ffi::c_return_unique_ptr_string()
     ));
-    check!(ffi::c_take_unique_ptr_vector_u8(
-        ffi::c_return_unique_ptr_vector_u8()
-    ));
+    let mut vector = ffi::c_return_unique_ptr_vector_u8();
+    assert_eq!(vector.pin_mut().pop(), Some(9));
+    check!(ffi::c_take_unique_ptr_vector_u8(vector));
     let mut vector = ffi::c_return_unique_ptr_vector_f64();
     vector.pin_mut().push(9.0);
     check!(ffi::c_take_unique_ptr_vector_f64(vector));