Repository: dtolnay/cxx (rust)

Issue:
Provide a `swap` implementation for types in cxxbridge.h
As recommended in #8. String, Str, and Box should have an efficient `swap`.

Pull request:
Add swap members + ADL functions
Fixes #58.

Adds `swap` members to each of `Slice`, `Str`, `Vec`, `Box`, and `String`. Also adds private friend functions so that Argument Dependent Lookup may function via the `using std::swap;` idiom.

Patch:
diff --git a/book/src/binding/box.md b/book/src/binding/box.md
index 91ccda49..5f2644b1 100644
--- a/book/src/binding/box.md
+++ b/book/src/binding/box.md
@@ -25,6 +25,8 @@ public:
   explicit Box(const T &);
   explicit Box(T &&);
 
+  void swap(Box &) noexcept;
+
   Box &operator=(const Box &);
   Box &operator=(Box &&) noexcept;
 
diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md
index fe1691e3..2354f538 100644
--- a/book/src/binding/slice.md
+++ b/book/src/binding/slice.md
@@ -32,6 +32,8 @@ public:
   size_t length() const noexcept;
   bool empty() const noexcept;
 
+  void swap(Slice & rhs) noexcept;
+
   T &operator[](size_t n) const noexcept;
   T &at(size_t n) const;
   T &front() const noexcept;
diff --git a/book/src/binding/str.md b/book/src/binding/str.md
index 999727c0..bc85909c 100644
--- a/book/src/binding/str.md
+++ b/book/src/binding/str.md
@@ -31,6 +31,8 @@ public:
   size_t size() const noexcept;
   size_t length() const noexcept;
 
+  void swap(Str & rhs) noexcept;
+
   using iterator = const char *;
   using const_iterator = const char *;
   const_iterator begin() const noexcept;
diff --git a/book/src/binding/string.md b/book/src/binding/string.md
index 8dc1ee4b..02790c35 100644
--- a/book/src/binding/string.md
+++ b/book/src/binding/string.md
@@ -35,6 +35,8 @@ public:
 
   const char *c_str() noexcept;
 
+  void swap(String & rhs) noexcept;
+
   using iterator = char *;
   iterator begin() noexcept;
   iterator end() noexcept;
diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md
index 39c1da19..e2af25c3 100644
--- a/book/src/binding/vec.md
+++ b/book/src/binding/vec.md
@@ -31,6 +31,8 @@ public:
   const T *data() const noexcept;
   T *data() noexcept;
   size_t capacity() const noexcept;
+  
+  void swap(Vec & rhs) noexcept;
 
   const T &operator[](size_t n) const noexcept;
   const T &at(size_t n) const;
diff --git a/include/cxx.h b/include/cxx.h
index d50045aa..8b47b96c 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -56,6 +56,8 @@ public:
 
   const char *c_str() noexcept;
 
+  void swap(String & rhs) noexcept;
+
   using iterator = char *;
   iterator begin() noexcept;
   iterator end() noexcept;
@@ -77,9 +79,14 @@ public:
   String(unsafe_bitcopy_t, const String &) noexcept;
 
 private:
+  friend void swap(String& lhs, String& rhs) noexcept {
+    lhs.swap(rhs);
+  }
+
   // Size and alignment statically verified by rust_string.rs.
   std::array<std::uintptr_t, 3> repr;
 };
+
 #endif // CXXBRIDGE1_RUST_STRING
 
 #ifndef CXXBRIDGE1_RUST_STR
@@ -101,6 +108,8 @@ public:
   std::size_t size() const noexcept;
   std::size_t length() const noexcept;
 
+  void swap(Str & rhs) noexcept;
+
   // Important in order for System V ABI to pass in registers.
   Str(const Str &) noexcept = default;
   ~Str() noexcept = default;
@@ -120,6 +129,10 @@ public:
   bool operator>=(const Str &) const noexcept;
 
 private:
+  friend void swap(Str & lhs, Str & rhs) noexcept {
+    lhs.swap(rhs);
+  }
+
   // Not necessarily ABI compatible with &str. Codegen will translate to
   // cxx::rust_str::RustStr which matches this layout.
   const char *ptr;
@@ -159,6 +172,8 @@ public:
   std::size_t length() const noexcept;
   bool empty() const noexcept;
 
+  void swap(Slice & rhs) noexcept;
+
   T &operator[](std::size_t n) const noexcept;
   T &at(std::size_t n) const;
   T &front() const noexcept;
@@ -173,6 +188,10 @@ public:
   iterator end() const noexcept;
 
 private:
+  friend void swap(Slice<T> & lhs, Slice<T> & rhs) noexcept {
+    lhs.swap(rhs);
+  }
+
   // Not necessarily ABI compatible with &[T]. Codegen will translate to
   // cxx::rust_slice::RustSlice which matches this layout.
   void *ptr;
@@ -235,6 +254,8 @@ public:
   explicit Box(const T &);
   explicit Box(T &&);
 
+  void swap(Box & rhs) noexcept;
+
   Box &operator=(const Box &);
   Box &operator=(Box &&) noexcept;
 
@@ -259,6 +280,11 @@ private:
   class allocation;
   Box(uninit) noexcept;
   void drop() noexcept;
+
+  friend void swap(Box & lhs, Box & rhs) noexcept {
+    lhs.swap(rhs);
+  }
+
   T *ptr;
 };
 #endif // CXXBRIDGE1_RUST_BOX
@@ -285,6 +311,8 @@ public:
   T *data() noexcept;
   std::size_t capacity() const noexcept;
 
+  void swap(Vec & rhs) noexcept;
+
   const T &operator[](std::size_t n) const noexcept;
   const T &at(std::size_t n) const;
   const T &front() const noexcept;
@@ -319,6 +347,10 @@ private:
   void set_len(std::size_t len) noexcept;
   void drop() noexcept;
 
+  friend void swap(Vec<T> & lhs, Vec<T> & rhs) noexcept {
+    lhs.swap(rhs);
+  }
+
   // Size and alignment statically verified by rust_vec.rs.
   std::array<std::uintptr_t, 3> repr;
 };
@@ -506,6 +538,13 @@ std::size_t Slice<T>::length() const noexcept {
   return this->len;
 }
 
+template <typename T>
+void Slice<T>::swap(Slice<T> & rhs) noexcept {
+  using std::swap;
+  swap(this->ptr, rhs.ptr);
+  swap(this->len, rhs.len);
+}
+
 template <typename T>
 bool Slice<T>::empty() const noexcept {
   return this->len == 0;
@@ -711,6 +750,12 @@ Box<T>::Box(T &&val) {
   alloc.ptr = nullptr;
 }
 
+template <typename T>
+void Box<T>::swap(Box<T> & rhs) noexcept {
+  using std::swap;
+  swap(this->ptr, rhs.ptr);
+}
+
 template <typename T>
 Box<T>::~Box() noexcept {
   if (this->ptr) {
@@ -842,6 +887,12 @@ T *Vec<T>::data() noexcept {
   return const_cast<T *>(const_cast<const Vec<T> *>(this)->data());
 }
 
+template <typename T>
+void Vec<T>::swap(Vec<T> & rhs) noexcept {
+  using std::swap;
+  swap(this->repr, rhs.repr);
+}
+
 template <typename T>
 const T &Vec<T>::operator[](std::size_t n) const noexcept {
   assert(n < this->size());
diff --git a/src/cxx.cc b/src/cxx.cc
index 6b71656d..a23fd8c5 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -130,6 +130,11 @@ const char *String::c_str() noexcept {
   return ptr;
 }
 
+void String::swap(String& rhs) noexcept {
+  using std::swap;
+  swap(this->repr, rhs.repr);
+}
+
 String::iterator String::begin() noexcept {
   return const_cast<char *>(this->data());
 }
@@ -210,6 +215,12 @@ Str::operator std::string() const {
   return std::string(this->data(), this->size());
 }
 
+void Str::swap(Str& rhs) noexcept {
+  using std::swap;
+  swap(this->ptr, rhs.ptr);
+  swap(this->len, rhs.len);
+}
+
 Str::const_iterator Str::begin() const noexcept { return this->cbegin(); }
 
 Str::const_iterator Str::end() const noexcept { return this->cend(); }


Test patch:
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index f043d817..67463d6f 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -752,8 +752,12 @@ extern "C" const char *cxx_run_test() noexcept {
   ASSERT(r->get() == 2020);
   ASSERT(r->set(2021) == 2021);
   ASSERT(r->get() == 2021);
-  ASSERT(r->set(2020) == 2020);
+  auto r2 = r_return_box();
+  swap(r, r2);
   ASSERT(r->get() == 2020);
+  ASSERT(r2->get() == 2021);
+
+
   ASSERT(std::string(Shared{0}.r_method_on_shared()) == "2020");
 
   ASSERT(std::string(rAliasedFunction(2020)) == "2020");
@@ -786,6 +790,23 @@ extern "C" const char *cxx_run_test() noexcept {
   ASSERT(strncmp(cstring.c_str(), "test", 5) == 0);
   ASSERT(cstring.length() == 4);
 
+  rust::String other_cstring = "foo";
+  swap(cstring, other_cstring);
+  ASSERT(cstring == "foo");
+  ASSERT(other_cstring == "test");
+
+  rust::Str cstr = "test";
+  rust::Str other_cstr = "foo";
+  swap(cstr, other_cstr);
+  ASSERT(cstr == "foo");
+  ASSERT(other_cstr == "test");
+
+  rust::Vec<int> vec1{1, 2};
+  rust::Vec<int> vec2{3, 4};
+  swap(vec1, vec2);
+  ASSERT(vec1[0] == 3 && vec1[1] == 4);
+  ASSERT(vec2[0] == 1 && vec2[1] == 2);
+
   cxx_test_suite_set_correct();
   return nullptr;
 }