Skip to content

Commit 3a6044b

Browse files
authored
Merge pull request #502 from dtolnay/mut
Support &mut [u8]
2 parents cd271f2 + 5945e56 commit 3a6044b

File tree

16 files changed

+140
-38
lines changed

16 files changed

+140
-38
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ returns of functions.
323323
<tr><td>String</td><td>rust::String</td><td></td></tr>
324324
<tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
325325
<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>
326+
<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>
326327
<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>
327328
<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>
328329
<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>

book/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
- [Reference: built-in bindings](bindings.md)
2626
- [String &mdash; rust::String](binding/string.md)
2727
- [&str &mdash; rust::Str](binding/str.md)
28-
- [&&#91;T&#93; &mdash; rust::Slice\<const T\>](binding/slice.md)
28+
- [&&#91;T&#93;, &mut &#91;T&#93; &mdash; rust::Slice\<T\>](binding/slice.md)
2929
- [CxxString &mdash; std::string](binding/cxxstring.md)
3030
- [Box\<T\> &mdash; rust::Box\<T\>](binding/box.md)
3131
- [UniquePtr\<T\> &mdash; std::unique\_ptr\<T\>](binding/uniqueptr.md)

book/src/binding/slice.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{{#title rust::Slice<T> — Rust ♡ C++}}
2-
# rust::Slice\<T\>
2+
# rust::Slice\<const T\>,&ensp;rust::Slice\<T\>
3+
4+
- Rust `&[T]` is written `rust::Slice<const T>` in C++
5+
- Rust `&mut [T]` is written `rust::Slice<T>` in C++
36

47
### Public API:
58

@@ -15,7 +18,10 @@ public:
1518
Slice(const Slice<T> &) noexcept;
1619
Slice(T *, size_t count) noexcept;
1720
21+
// if std::is_const<T> {
1822
Slice &operator=(const Slice<T> &) noexcept;
23+
// }
24+
Slice &operator=(Slice<T> &&) noexcept;
1925
2026
T *data() const noexcept;
2127
size_t size() const noexcept;
@@ -27,11 +33,14 @@ public:
2733

2834
### Restrictions:
2935

30-
Only &amp;\[u8\] i.e. rust::Slice\<const uint8\_t\> is currently implemented.
31-
Support for arbitrary &amp;\[T\] is coming.
36+
Only T=u8 i.e. rust::Slice\<const uint8\_t\> and rust::Slice\<uint8\_t\> are
37+
currently implemented. Support for arbitrary T is coming.
3238

3339
Allowed as function argument or return value. Not supported in shared structs.
34-
&amp;mut \[T\] is not supported yet.
40+
41+
Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are
42+
move-assignable.) You'll need to write std::move occasionally as a reminder that
43+
accidentally exposing overlapping &amp;mut \[T\] to Rust is UB.
3544

3645
## Example
3746

book/src/bindings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ returns of extern functions.
1212
<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>
1313
<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>
1414
<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>
15+
<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>
1516
<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>
1617
<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>
1718
<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.
4344

4445
<table>
4546
<tr><th>name in Rust</th><th>name in C++</th></tr>
46-
<tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td></tr>
4747
<tr><td>BTreeMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
4848
<tr><td>HashMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
4949
<tr><td>Arc&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>

gen/src/builtin.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub(super) fn write(out: &mut OutFile) {
149149
if builtin.ptr_len {
150150
out.begin_block(Block::Namespace("repr"));
151151
writeln!(out, "struct PtrLen final {{");
152-
writeln!(out, " const void *ptr;");
152+
writeln!(out, " void *ptr;");
153153
writeln!(out, " size_t len;");
154154
writeln!(out, "}};");
155155
out.end_block(Block::Namespace("repr"));
@@ -173,7 +173,10 @@ pub(super) fn write(out: &mut OutFile) {
173173
}
174174
if builtin.rust_str_repr {
175175
writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{");
176-
writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};");
176+
writeln!(
177+
out,
178+
" return repr::PtrLen{{const_cast<char *>(str.ptr), str.len}};",
179+
);
177180
writeln!(out, " }}");
178181
}
179182
writeln!(out, "}};");
@@ -189,18 +192,21 @@ pub(super) fn write(out: &mut OutFile) {
189192
out,
190193
" static Slice<T> slice(repr::PtrLen repr) noexcept {{",
191194
);
192-
writeln!(
193-
out,
194-
" return {{static_cast<const T *>(repr.ptr), repr.len}};",
195-
);
195+
writeln!(out, " return {{static_cast<T *>(repr.ptr), repr.len}};");
196196
writeln!(out, " }}");
197197
}
198198
if builtin.rust_slice_repr {
199+
include.type_traits = true;
199200
writeln!(
200201
out,
201202
" static repr::PtrLen repr(Slice<T> slice) noexcept {{",
202203
);
203-
writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};");
204+
writeln!(out, " return repr::PtrLen{{");
205+
writeln!(
206+
out,
207+
" const_cast<typename ::std::remove_const<T>::type *>(slice.ptr),",
208+
);
209+
writeln!(out, " slice.len}};");
204210
writeln!(out, " }}");
205211
}
206212
writeln!(out, "}};");

gen/src/write.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
436436
out.builtin.rust_str_repr = true;
437437
write!(out, "::rust::impl<::rust::Str>::repr(");
438438
}
439-
Some(Type::SliceRefU8(_)) if !indirect_return => {
439+
Some(ty @ Type::SliceRefU8(_)) if !indirect_return => {
440440
out.builtin.rust_slice_repr = true;
441-
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(")
441+
write!(out, "::rust::impl<");
442+
write_type(out, ty);
443+
write!(out, ">::repr(");
442444
}
443445
_ => {}
444446
}
@@ -474,12 +476,13 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
474476
out.builtin.unsafe_bitcopy = true;
475477
write_type(out, &arg.ty);
476478
write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
477-
} else if let Type::SliceRefU8(_) = arg.ty {
478-
write!(
479-
out,
480-
"::rust::Slice<const uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
481-
arg.ident,
482-
);
479+
} else if let Type::SliceRefU8(slice) = &arg.ty {
480+
write_type(out, &arg.ty);
481+
write!(out, "(static_cast<");
482+
if slice.mutability.is_none() {
483+
write!(out, "const ");
484+
}
485+
write!(out, "uint8_t *>({0}.ptr), {0}.len)", arg.ident);
483486
} else if out.types.needs_indirect_abi(&arg.ty) {
484487
out.include.utility = true;
485488
write!(out, "::std::move(*{})", arg.ident);
@@ -507,7 +510,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
507510
writeln!(out, " throw$.len = ::std::strlen(catch$);");
508511
writeln!(
509512
out,
510-
" throw$.ptr = ::cxxbridge1$exception(catch$, throw$.len);",
513+
" throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
511514
);
512515
writeln!(out, " }});");
513516
writeln!(out, " return throw$;");
@@ -694,7 +697,9 @@ fn write_rust_function_shim_impl(
694697
}
695698
Type::SliceRefU8(_) => {
696699
out.builtin.rust_slice_new = true;
697-
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::slice(");
700+
write!(out, "::rust::impl<");
701+
write_type(out, ret);
702+
write!(out, ">::slice(");
698703
}
699704
_ => {}
700705
}
@@ -720,7 +725,9 @@ fn write_rust_function_shim_impl(
720725
}
721726
Type::SliceRefU8(_) => {
722727
out.builtin.rust_slice_repr = true;
723-
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(");
728+
write!(out, "::rust::impl<");
729+
write_type(out, &arg.ty);
730+
write!(out, ">::repr(");
724731
}
725732
ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
726733
_ => {}
@@ -890,8 +897,12 @@ fn write_type(out: &mut OutFile, ty: &Type) {
890897
Type::Str(_) => {
891898
write!(out, "::rust::Str");
892899
}
893-
Type::SliceRefU8(_) => {
894-
write!(out, "::rust::Slice<const uint8_t>");
900+
Type::SliceRefU8(ty) => {
901+
write!(out, "::rust::Slice<");
902+
if ty.mutability.is_none() {
903+
write!(out, "const ");
904+
}
905+
write!(out, "uint8_t>");
895906
}
896907
Type::Fn(f) => {
897908
write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });

include/cxx.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,28 @@ class Str final {
8787
#endif // CXXBRIDGE1_RUST_STR
8888

8989
#ifndef CXXBRIDGE1_RUST_SLICE
90-
template <typename T>
91-
class Slice final {
92-
static_assert(std::is_const<T>::value,
93-
"&[T] needs to be written as rust::Slice<const T> in C++");
90+
namespace detail {
91+
template <bool cond>
92+
struct copy_assignable_if {};
93+
94+
template <>
95+
struct copy_assignable_if<false> {
96+
copy_assignable_if() noexcept = default;
97+
copy_assignable_if(const copy_assignable_if &) noexcept = default;
98+
copy_assignable_if &operator=(const copy_assignable_if &) noexcept = delete;
99+
copy_assignable_if &operator=(copy_assignable_if &&) noexcept = default;
100+
};
101+
} // namespace detail
94102

103+
template <typename T>
104+
class Slice final
105+
: private detail::copy_assignable_if<std::is_const<T>::value> {
95106
public:
96107
Slice() noexcept;
97108
Slice(T *, size_t count) noexcept;
98109

99110
Slice &operator=(const Slice<T> &) noexcept = default;
111+
Slice &operator=(Slice<T> &&) noexcept = default;
100112

101113
T *data() const noexcept;
102114
size_t size() const noexcept;

macro/src/expand.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
330330
_ => quote!(#var),
331331
},
332332
Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
333-
Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
333+
Type::SliceRefU8(ty) => match ty.mutable {
334+
false => quote!(::cxx::private::RustSliceU8::from_ref(#var)),
335+
true => quote!(::cxx::private::RustSliceU8::from_mut(#var)),
336+
},
334337
ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
335338
_ => quote!(#var),
336339
}
@@ -423,7 +426,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
423426
_ => call,
424427
},
425428
Type::Str(_) => quote!(#call.as_str()),
426-
Type::SliceRefU8(_) => quote!(#call.as_slice()),
429+
Type::SliceRefU8(ty) => match ty.mutable {
430+
false => quote!(#call.as_slice()),
431+
true => quote!(#call.as_mut_slice()),
432+
},
427433
_ => call,
428434
},
429435
};
@@ -610,7 +616,10 @@ fn expand_rust_function_shim_impl(
610616
_ => quote!(#ident),
611617
},
612618
Type::Str(_) => quote!(#ident.as_str()),
613-
Type::SliceRefU8(_) => quote!(#ident.as_slice()),
619+
Type::SliceRefU8(ty) => match ty.mutable {
620+
false => quote!(#ident.as_slice()),
621+
true => quote!(#ident.as_mut_slice()),
622+
},
614623
ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
615624
_ => quote!(#ident),
616625
}
@@ -654,7 +663,10 @@ fn expand_rust_function_shim_impl(
654663
_ => None,
655664
},
656665
Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
657-
Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
666+
Type::SliceRefU8(ty) => match ty.mutable {
667+
false => Some(quote!(::cxx::private::RustSliceU8::from_ref)),
668+
true => Some(quote!(::cxx::private::RustSliceU8::from_mut)),
669+
},
658670
_ => None,
659671
});
660672

src/cxx.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,17 @@ static_assert(std::is_trivially_move_assignable<Slice<const uint8_t>>::value,
180180
static_assert(std::is_trivially_destructible<Slice<const uint8_t>>::value,
181181
"trivial ~Slice()");
182182

183+
static_assert(std::is_trivially_copy_constructible<Slice<uint8_t>>::value,
184+
"trivial Slice(const Slice &)");
185+
static_assert(std::is_trivially_move_constructible<Slice<uint8_t>>::value,
186+
"trivial Slice(Slice &&)");
187+
static_assert(!std::is_copy_assignable<Slice<uint8_t>>::value,
188+
"delete Slice::operator=(const Slice &) for mut slices");
189+
static_assert(std::is_trivially_move_assignable<Slice<uint8_t>>::value,
190+
"trivial Slice::operator=(Slice &&)");
191+
static_assert(std::is_trivially_destructible<Slice<uint8_t>>::value,
192+
"trivial ~Slice()");
193+
183194
extern "C" {
184195
const char *cxxbridge1$error(const char *ptr, size_t len) {
185196
char *copy = new char[len];

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@
332332
//! <tr><td>String</td><td>rust::String</td><td></td></tr>
333333
//! <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
334334
//! <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>
335+
//! <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>
335336
//! <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>
336337
//! <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>
337338
//! <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>

src/rust_sliceu8.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,27 @@ pub struct RustSliceU8 {
1111
}
1212

1313
impl RustSliceU8 {
14-
pub fn from(s: &[u8]) -> Self {
14+
pub fn from_ref(s: &[u8]) -> Self {
1515
RustSliceU8 {
1616
ptr: NonNull::from(s).cast::<u8>(),
1717
len: s.len(),
1818
}
1919
}
2020

21+
pub fn from_mut(s: &mut [u8]) -> Self {
22+
RustSliceU8 {
23+
len: s.len(),
24+
ptr: NonNull::from(s).cast::<u8>(),
25+
}
26+
}
27+
2128
pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
2229
slice::from_raw_parts(self.ptr.as_ptr(), self.len)
2330
}
31+
32+
pub unsafe fn as_mut_slice<'a>(self) -> &'a mut [u8] {
33+
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
34+
}
2435
}
2536

2637
const_assert_eq!(

syntax/check.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) {
178178
}
179179

180180
fn check_type_slice(cx: &mut Check, ty: &Slice) {
181-
cx.error(ty, "only &[u8] is supported so far, not other slice types");
181+
cx.error(
182+
ty,
183+
"only &[u8] and &mut [u8] are supported so far, not other slice types",
184+
);
182185
}
183186

184187
fn check_type_fn(cx: &mut Check, ty: &Signature) {
@@ -489,7 +492,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
489492
Type::Str(_) => "&str".to_owned(),
490493
Type::CxxVector(_) => "C++ vector".to_owned(),
491494
Type::Slice(_) => "slice".to_owned(),
492-
Type::SliceRefU8(_) => "&[u8]".to_owned(),
495+
Type::SliceRefU8(ty) => match ty.mutable {
496+
false => "&[u8]".to_owned(),
497+
true => "&mut [u8]".to_owned(),
498+
},
493499
Type::Fn(_) => "function pointer".to_owned(),
494500
Type::Void(_) => "()".to_owned(),
495501
}

syntax/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Typ
658658
}
659659
}
660660
Type::Slice(slice) => match &slice.inner {
661-
Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
661+
Type::Ident(ident) if ident.rust == U8 => Type::SliceRefU8,
662662
_ => Type::Ref,
663663
},
664664
_ => Type::Ref,

0 commit comments

Comments
 (0)