Repository: dtolnay/cxx (language: rust)

Issue:
&mut [u8] support
This seems to work, but I have no idea if it's the correct way to implement it.

Closes #451.


Discussion / hints:
I mean, you could just copy it by getting the data pointer and constructing another slice from it anyway. But yeah, technically that is UB so maybe the copy constructor shouldn't exist.

Oh yeah, being able to reuse the Slice type for both mutable and immutable slices would be pretty nice. 
Hmm, the tests are failing because rust::repr::PtrLen has a `const void*` as the pointer type which it's trying to cast to `uint8_t*`. How should this be fixed?

Edit: also not seeing how to have the copy constructor only be available for `Slice<const T>` after #453. I tried messing around with std::enable_if/SFINAE but this is beyond my limited C++ knowledge :P
> Hmm, the tests are failing because rust::repr::PtrLen has a `const void*` as the pointer type which it's trying to cast to `uint8_t*`. How should this be fixed?

Looks like a job for https://en.cppreference.com/w/cpp/language/const_cast. Alternatively maybe if PtrLen were to contain `void*` it would work in both cases?

> Edit: also not seeing how to have the copy constructor only be available for `Slice<const T>` after #453. I tried messing around with std::enable_if/SFINAE but this is beyond my limited C++ knowledge :P

Yeah something with enable_if. I'm not a C++ pro either, one of us will just need to poke on it until it works. -.-
Well, this should work now. At least the tests pass, I can't check it with real code yet though because of #496.
I can't get the enable_if on the slice constructors to work for some reason, and at this point I think I tried 7 different ways of doing it. It either works for both mutable and const slices or not at all. At this point I'd just leave it since C++ is already unsafe (even if that constructor was gone, you could still trivially copy mutable slices) unless you really want this check.

Everything else seems to work now though, after testing it with [the project I'm using cxx with][1].

[1]: https://github.com/2xsaiko/mcrestool

Fix patch:
diff --git a/README.md b/README.md
index bc7600640..e4ac67e64 100644
--- a/README.md
+++ b/README.md
@@ -323,6 +323,7 @@ returns of functions.
 <tr><td>String</td><td>rust::String</td><td></td></tr>
 <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
 <tr><td>&amp;[u8]</td><td>rust::Slice&lt;const uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
+<tr><td>&amp;mut [u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
 <tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index 31cbb4624..e10c2ccbd 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -25,7 +25,7 @@
 - [Reference: built-in bindings](bindings.md)
     - [String &mdash; rust::String](binding/string.md)
     - [&str &mdash; rust::Str](binding/str.md)
-    - [&&#91;T&#93; &mdash; rust::Slice\<const T\>](binding/slice.md)
+    - [&&#91;T&#93;, &mut &#91;T&#93; &mdash; rust::Slice\<T\>](binding/slice.md)
     - [CxxString &mdash; std::string](binding/cxxstring.md)
     - [Box\<T\> &mdash; rust::Box\<T\>](binding/box.md)
     - [UniquePtr\<T\> &mdash; std::unique\_ptr\<T\>](binding/uniqueptr.md)
diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md
index aacd56b2b..4da7d7397 100644
--- a/book/src/binding/slice.md
+++ b/book/src/binding/slice.md
@@ -1,5 +1,8 @@
 {{#title rust::Slice<T> — Rust ♡ C++}}
-# rust::Slice\<T\>
+# rust::Slice\<const T\>,&ensp;rust::Slice\<T\>
+
+- Rust `&[T]` is written `rust::Slice<const T>` in C++
+- Rust `&mut [T]` is written `rust::Slice<T>` in C++
 
 ### Public API:
 
@@ -15,7 +18,10 @@ public:
   Slice(const Slice<T> &) noexcept;
   Slice(T *, size_t count) noexcept;
 
+  // if std::is_const<T> {
   Slice &operator=(const Slice<T> &) noexcept;
+  // }
+  Slice &operator=(Slice<T> &&) noexcept;
 
   T *data() const noexcept;
   size_t size() const noexcept;
@@ -27,11 +33,14 @@ public:
 
 ### Restrictions:
 
-Only &amp;\[u8\] i.e. rust::Slice\<const uint8\_t\> is currently implemented.
-Support for arbitrary &amp;\[T\] is coming.
+Only T=u8 i.e. rust::Slice\<const uint8\_t\> and rust::Slice\<uint8\_t\> are
+currently implemented. Support for arbitrary T is coming.
 
 Allowed as function argument or return value. Not supported in shared structs.
-&amp;mut \[T\] is not supported yet.
+
+Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are
+move-assignable.) You'll need to write std::move occasionally as a reminder that
+accidentally exposing overlapping &amp;mut \[T\] to Rust is UB.
 
 ## Example
 
diff --git a/book/src/bindings.md b/book/src/bindings.md
index f19dc5d13..df6a8d7c4 100644
--- a/book/src/bindings.md
+++ b/book/src/bindings.md
@@ -12,6 +12,7 @@ returns of extern functions.
 <tr><td style="padding:3px 6px">String</td><td style="padding:3px 6px"><b><a href="binding/string.md">rust::String</a></b></td><td style="padding:3px 6px"></td></tr>
 <tr><td style="padding:3px 6px">&amp;str</td><td style="padding:3px 6px"><b><a href="binding/str.md">rust::Str</a></b></td><td style="padding:3px 6px"></td></tr>
 <tr><td style="padding:3px 6px">&amp;[u8]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;const&nbsp;uint8_t&gt;</a></b></td><td style="padding:3px 6px"><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
+<tr><td style="padding:3px 6px">&amp;mut [u8]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;uint8_t&gt;</a></b></td><td style="padding:3px 6px"><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
 <tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
 <tr><td style="padding:3px 6px">Box&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::unique_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
@@ -43,7 +44,6 @@ matter of designing a nice API for each in its non-native language.
 
 <table>
 <tr><th>name in Rust</th><th>name in C++</th></tr>
-<tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td></tr>
 <tr><td>BTreeMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td>HashMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td>Arc&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index 9c5f4fd78..53facbfed 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -149,7 +149,7 @@ pub(super) fn write(out: &mut OutFile) {
     if builtin.ptr_len {
         out.begin_block(Block::Namespace("repr"));
         writeln!(out, "struct PtrLen final {{");
-        writeln!(out, "  const void *ptr;");
+        writeln!(out, "  void *ptr;");
         writeln!(out, "  size_t len;");
         writeln!(out, "}};");
         out.end_block(Block::Namespace("repr"));
@@ -173,7 +173,10 @@ pub(super) fn write(out: &mut OutFile) {
         }
         if builtin.rust_str_repr {
             writeln!(out, "  static repr::PtrLen repr(Str str) noexcept {{");
-            writeln!(out, "    return repr::PtrLen{{str.ptr, str.len}};");
+            writeln!(
+                out,
+                "    return repr::PtrLen{{const_cast<char *>(str.ptr), str.len}};",
+            );
             writeln!(out, "  }}");
         }
         writeln!(out, "}};");
@@ -189,18 +192,21 @@ pub(super) fn write(out: &mut OutFile) {
                 out,
                 "  static Slice<T> slice(repr::PtrLen repr) noexcept {{",
             );
-            writeln!(
-                out,
-                "    return {{static_cast<const T *>(repr.ptr), repr.len}};",
-            );
+            writeln!(out, "    return {{static_cast<T *>(repr.ptr), repr.len}};");
             writeln!(out, "  }}");
         }
         if builtin.rust_slice_repr {
+            include.type_traits = true;
             writeln!(
                 out,
                 "  static repr::PtrLen repr(Slice<T> slice) noexcept {{",
             );
-            writeln!(out, "    return repr::PtrLen{{slice.ptr, slice.len}};");
+            writeln!(out, "    return repr::PtrLen{{");
+            writeln!(
+                out,
+                "        const_cast<typename ::std::remove_const<T>::type *>(slice.ptr),",
+            );
+            writeln!(out, "        slice.len}};");
             writeln!(out, "  }}");
         }
         writeln!(out, "}};");
diff --git a/gen/src/write.rs b/gen/src/write.rs
index d99778605..8b512daf2 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -436,9 +436,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
             out.builtin.rust_str_repr = true;
             write!(out, "::rust::impl<::rust::Str>::repr(");
         }
-        Some(Type::SliceRefU8(_)) if !indirect_return => {
+        Some(ty @ Type::SliceRefU8(_)) if !indirect_return => {
             out.builtin.rust_slice_repr = true;
-            write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(")
+            write!(out, "::rust::impl<");
+            write_type(out, ty);
+            write!(out, ">::repr(");
         }
         _ => {}
     }
@@ -474,12 +476,13 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
             out.builtin.unsafe_bitcopy = true;
             write_type(out, &arg.ty);
             write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
-        } else if let Type::SliceRefU8(_) = arg.ty {
-            write!(
-                out,
-                "::rust::Slice<const uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
-                arg.ident,
-            );
+        } else if let Type::SliceRefU8(slice) = &arg.ty {
+            write_type(out, &arg.ty);
+            write!(out, "(static_cast<");
+            if slice.mutability.is_none() {
+                write!(out, "const ");
+            }
+            write!(out, "uint8_t *>({0}.ptr), {0}.len)", arg.ident);
         } else if out.types.needs_indirect_abi(&arg.ty) {
             out.include.utility = true;
             write!(out, "::std::move(*{})", arg.ident);
@@ -507,7 +510,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
         writeln!(out, "        throw$.len = ::std::strlen(catch$);");
         writeln!(
             out,
-            "        throw$.ptr = ::cxxbridge1$exception(catch$, throw$.len);",
+            "        throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
         );
         writeln!(out, "      }});");
         writeln!(out, "  return throw$;");
@@ -694,7 +697,9 @@ fn write_rust_function_shim_impl(
             }
             Type::SliceRefU8(_) => {
                 out.builtin.rust_slice_new = true;
-                write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::slice(");
+                write!(out, "::rust::impl<");
+                write_type(out, ret);
+                write!(out, ">::slice(");
             }
             _ => {}
         }
@@ -720,7 +725,9 @@ fn write_rust_function_shim_impl(
             }
             Type::SliceRefU8(_) => {
                 out.builtin.rust_slice_repr = true;
-                write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(");
+                write!(out, "::rust::impl<");
+                write_type(out, &arg.ty);
+                write!(out, ">::repr(");
             }
             ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
             _ => {}
@@ -890,8 +897,12 @@ fn write_type(out: &mut OutFile, ty: &Type) {
         Type::Str(_) => {
             write!(out, "::rust::Str");
         }
-        Type::SliceRefU8(_) => {
-            write!(out, "::rust::Slice<const uint8_t>");
+        Type::SliceRefU8(ty) => {
+            write!(out, "::rust::Slice<");
+            if ty.mutability.is_none() {
+                write!(out, "const ");
+            }
+            write!(out, "uint8_t>");
         }
         Type::Fn(f) => {
             write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });
diff --git a/include/cxx.h b/include/cxx.h
index 812278686..1b30497a2 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -87,16 +87,28 @@ class Str final {
 #endif // CXXBRIDGE1_RUST_STR
 
 #ifndef CXXBRIDGE1_RUST_SLICE
-template <typename T>
-class Slice final {
-  static_assert(std::is_const<T>::value,
-                "&[T] needs to be written as rust::Slice<const T> in C++");
+namespace detail {
+template <bool cond>
+struct copy_assignable_if {};
+
+template <>
+struct copy_assignable_if<false> {
+  copy_assignable_if() noexcept = default;
+  copy_assignable_if(const copy_assignable_if &) noexcept = default;
+  copy_assignable_if &operator=(const copy_assignable_if &) noexcept = delete;
+  copy_assignable_if &operator=(copy_assignable_if &&) noexcept = default;
+};
+} // namespace detail
 
+template <typename T>
+class Slice final
+    : private detail::copy_assignable_if<std::is_const<T>::value> {
 public:
   Slice() noexcept;
   Slice(T *, size_t count) noexcept;
 
   Slice &operator=(const Slice<T> &) noexcept = default;
+  Slice &operator=(Slice<T> &&) noexcept = default;
 
   T *data() const noexcept;
   size_t size() const noexcept;
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index f29c4cc1e..1fee9286e 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -330,7 +330,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
                 _ => quote!(#var),
             },
             Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
-            Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
+            Type::SliceRefU8(ty) => match ty.mutable {
+                false => quote!(::cxx::private::RustSliceU8::from_ref(#var)),
+                true => quote!(::cxx::private::RustSliceU8::from_mut(#var)),
+            },
             ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
             _ => quote!(#var),
         }
@@ -423,7 +426,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
                     _ => call,
                 },
                 Type::Str(_) => quote!(#call.as_str()),
-                Type::SliceRefU8(_) => quote!(#call.as_slice()),
+                Type::SliceRefU8(ty) => match ty.mutable {
+                    false => quote!(#call.as_slice()),
+                    true => quote!(#call.as_mut_slice()),
+                },
                 _ => call,
             },
         };
@@ -610,7 +616,10 @@ fn expand_rust_function_shim_impl(
                 _ => quote!(#ident),
             },
             Type::Str(_) => quote!(#ident.as_str()),
-            Type::SliceRefU8(_) => quote!(#ident.as_slice()),
+            Type::SliceRefU8(ty) => match ty.mutable {
+                false => quote!(#ident.as_slice()),
+                true => quote!(#ident.as_mut_slice()),
+            },
             ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
             _ => quote!(#ident),
         }
@@ -654,7 +663,10 @@ fn expand_rust_function_shim_impl(
             _ => None,
         },
         Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
-        Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
+        Type::SliceRefU8(ty) => match ty.mutable {
+            false => Some(quote!(::cxx::private::RustSliceU8::from_ref)),
+            true => Some(quote!(::cxx::private::RustSliceU8::from_mut)),
+        },
         _ => None,
     });
 
diff --git a/src/cxx.cc b/src/cxx.cc
index 923b6412d..28506ef6a 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -180,6 +180,17 @@ static_assert(std::is_trivially_move_assignable<Slice<const uint8_t>>::value,
 static_assert(std::is_trivially_destructible<Slice<const uint8_t>>::value,
               "trivial ~Slice()");
 
+static_assert(std::is_trivially_copy_constructible<Slice<uint8_t>>::value,
+              "trivial Slice(const Slice &)");
+static_assert(std::is_trivially_move_constructible<Slice<uint8_t>>::value,
+              "trivial Slice(Slice &&)");
+static_assert(!std::is_copy_assignable<Slice<uint8_t>>::value,
+              "delete Slice::operator=(const Slice &) for mut slices");
+static_assert(std::is_trivially_move_assignable<Slice<uint8_t>>::value,
+              "trivial Slice::operator=(Slice &&)");
+static_assert(std::is_trivially_destructible<Slice<uint8_t>>::value,
+              "trivial ~Slice()");
+
 extern "C" {
 const char *cxxbridge1$error(const char *ptr, size_t len) {
   char *copy = new char[len];
diff --git a/src/lib.rs b/src/lib.rs
index 0427af699..d1c894ee7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -332,6 +332,7 @@
 //! <tr><td>String</td><td>rust::String</td><td></td></tr>
 //! <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
 //! <tr><td>&amp;[u8]</td><td>rust::Slice&lt;const uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
+//! <tr><td>&amp;mut [u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
 //! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 //! <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs
index 32f8798bd..e14f98859 100644
--- a/src/rust_sliceu8.rs
+++ b/src/rust_sliceu8.rs
@@ -11,16 +11,27 @@ pub struct RustSliceU8 {
 }
 
 impl RustSliceU8 {
-    pub fn from(s: &[u8]) -> Self {
+    pub fn from_ref(s: &[u8]) -> Self {
         RustSliceU8 {
             ptr: NonNull::from(s).cast::<u8>(),
             len: s.len(),
         }
     }
 
+    pub fn from_mut(s: &mut [u8]) -> Self {
+        RustSliceU8 {
+            len: s.len(),
+            ptr: NonNull::from(s).cast::<u8>(),
+        }
+    }
+
     pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
         slice::from_raw_parts(self.ptr.as_ptr(), self.len)
     }
+
+    pub unsafe fn as_mut_slice<'a>(self) -> &'a mut [u8] {
+        slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
+    }
 }
 
 const_assert_eq!(
diff --git a/syntax/check.rs b/syntax/check.rs
index 2e94f4db2..1ec6178c1 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -178,7 +178,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) {
 }
 
 fn check_type_slice(cx: &mut Check, ty: &Slice) {
-    cx.error(ty, "only &[u8] is supported so far, not other slice types");
+    cx.error(
+        ty,
+        "only &[u8] and &mut [u8] are supported so far, not other slice types",
+    );
 }
 
 fn check_type_fn(cx: &mut Check, ty: &Signature) {
@@ -489,7 +492,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
         Type::Str(_) => "&str".to_owned(),
         Type::CxxVector(_) => "C++ vector".to_owned(),
         Type::Slice(_) => "slice".to_owned(),
-        Type::SliceRefU8(_) => "&[u8]".to_owned(),
+        Type::SliceRefU8(ty) => match ty.mutable {
+            false => "&[u8]".to_owned(),
+            true => "&mut [u8]".to_owned(),
+        },
         Type::Fn(_) => "function pointer".to_owned(),
         Type::Void(_) => "()".to_owned(),
     }
diff --git a/syntax/parse.rs b/syntax/parse.rs
index 38b3dccf4..c6d64804d 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -658,7 +658,7 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Typ
             }
         }
         Type::Slice(slice) => match &slice.inner {
-            Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
+            Type::Ident(ident) if ident.rust == U8 => Type::SliceRefU8,
             _ => Type::Ref,
         },
         _ => Type::Ref,


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 762af4d67..af72fb46d 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -77,6 +77,7 @@ pub mod ffi {
         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_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
         fn c_return_rust_string() -> String;
         fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
         fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
@@ -140,6 +141,7 @@ pub mod ffi {
         fn c_try_return_ref(s: &String) -> Result<&String>;
         fn c_try_return_str(s: &str) -> Result<&str>;
         fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+        fn c_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
         fn c_try_return_rust_string() -> Result<String>;
         fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
         fn c_try_return_rust_vec() -> Result<Vec<u8>>;
@@ -187,6 +189,7 @@ pub mod ffi {
         fn r_return_mut(shared: &mut Shared) -> &mut usize;
         fn r_return_str(shared: &Shared) -> &str;
         fn r_return_sliceu8(shared: &Shared) -> &[u8];
+        fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
         fn r_return_rust_string() -> String;
         fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
         fn r_return_rust_vec() -> Vec<u8>;
@@ -220,6 +223,7 @@ pub mod ffi {
         fn r_try_return_box() -> Result<Box<R>>;
         fn r_fail_return_primitive() -> Result<usize>;
         fn r_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+        fn r_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
 
         fn get(self: &R) -> usize;
         fn set(self: &mut R, n: usize) -> usize;
@@ -371,6 +375,10 @@ fn r_return_sliceu8(shared: &ffi::Shared) -> &[u8] {
     b"2020"
 }
 
+fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8] {
+    slice
+}
+
 fn r_return_rust_string() -> String {
     "2020".to_owned()
 }
@@ -509,6 +517,10 @@ fn r_try_return_sliceu8(slice: &[u8]) -> Result<&[u8], Error> {
     Ok(slice)
 }
 
+fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> {
+    Ok(slice)
+}
+
 fn r_aliased_function(x: i32) -> String {
     x.to_string()
 }
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index f1c293de9..238b1457d 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -77,6 +77,10 @@ rust::Slice<const uint8_t> c_return_sliceu8(const Shared &shared) {
       reinterpret_cast<const uint8_t *>(SLICE_DATA), sizeof(SLICE_DATA));
 }
 
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice) {
+  return slice;
+}
+
 rust::String c_return_rust_string() { return "2020"; }
 
 std::unique_ptr<std::string> c_return_unique_ptr_string() {
@@ -435,6 +439,10 @@ rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t> s) {
   return s;
 }
 
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t> s) {
+  return s;
+}
+
 rust::String c_try_return_rust_string() { return c_return_rust_string(); }
 
 std::unique_ptr<std::string> c_try_return_unique_ptr_string() {
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 5acf469db..6c288d724 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -84,6 +84,7 @@ const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared);
 size_t &c_return_mut(Shared &shared);
 rust::Str c_return_str(const Shared &shared);
 rust::Slice<const uint8_t> c_return_sliceu8(const Shared &shared);
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice);
 rust::String c_return_rust_string();
 std::unique_ptr<std::string> c_return_unique_ptr_string();
 std::unique_ptr<std::vector<uint8_t>> c_return_unique_ptr_vector_u8();
@@ -148,6 +149,7 @@ rust::Box<R> c_try_return_box();
 const rust::String &c_try_return_ref(const rust::String &);
 rust::Str c_try_return_str(rust::Str);
 rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t>);
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t>);
 rust::String c_try_return_rust_string();
 std::unique_ptr<std::string> c_try_return_unique_ptr_string();
 rust::Vec<uint8_t> c_try_return_rust_vec();