Repository: dtolnay/cxx (rust)

Issue:
Allow usage of &mut [u8] in FFI
<!--  ❤️  -->

Use case: I want to pass `read(&mut self, &mut [u8])` to C++, for that I would need some kind of mutable slice type (or the C-like `buf: *mut u8, off: usize, len: usize`, but raw pointers are also not allowed yet). I'm already trying to implement this so I'll submit a PR if I get it to work.

Pull request:
Support &mut [u8]
Closes #451. Closes #452.

Patch:
diff --git a/README.md b/README.md
index bc760064..e4ac67e6 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 31cbb462..e10c2ccb 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 aacd56b2..4da7d739 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 f19dc5d1..df6a8d7c 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 9c5f4fd7..53facbfe 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 d9977860..8b512daf 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 81227868..1b30497a 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -87,16 +87,28 @@ private:
 #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 f29c4cc1..1fee9286 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 923b6412..28506ef6 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 0427af69..d1c894ee 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 32f8798b..e14f9885 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 2e94f4db..1ec6178c 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 38b3dccf..c6d64804 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 762af4d6..af72fb46 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 f1c293de..238b1457 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 5acf469d..6c288d72 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();