diff --git a/README.md b/README.md index 0b3aab317..ad14b574f 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ returns of functions. name in Rustname in C++restrictions Stringrust::String &strrust::Str +&[u8]rust::Slice<uint8_t>(no other slice types currently supported) CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type @@ -316,7 +317,6 @@ matter of designing a nice API for each in its non-native language. - diff --git a/gen/write.rs b/gen/write.rs index 4eed1b40a..3f3cf4b65 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -365,6 +365,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, "::rust::Slice::Repr("), _ => {} } write!(out, "{}$(", efn.ident); @@ -395,7 +396,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) if !indirect_return => write!(out, ")"), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -566,6 +567,7 @@ fn write_rust_function_shim_impl( } match &arg.ty { Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} } @@ -573,7 +575,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) => write!(out, ")"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -637,6 +639,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } } @@ -645,7 +648,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} - Type::Str(_) => write!(out, " "), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), _ => write_space_after_type(out, ty), } } @@ -664,6 +667,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), + Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -676,6 +680,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -721,9 +726,16 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &r.inner); write!(out, " &"); } + Type::Slice(_) => { + // For now, only U8 slices are supported, which are covered separately below + unreachable!() + } Type::Str(_) => { write!(out, "::rust::Str"); } + Type::SliceRefU8(_) => { + write!(out, "::rust::Slice"); + } Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { @@ -750,11 +762,11 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::Fn(_) => { + Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) | Type::Fn(_) => { write!(out, " ") } Type::Ref(_) => {} - Type::Void(_) => unreachable!(), + Type::Void(_) | Type::Slice(_) => unreachable!(), } } diff --git a/include/cxx.h b/include/cxx.h index a8239a122..57a6e24b7 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -16,6 +16,43 @@ inline namespace cxxbridge02 { struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_SLICE +#define CXXBRIDGE02_RUST_SLICE +template +class Slice final { +public: + Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + Slice(const Slice &) noexcept = default; + + Slice(const T* s, size_t size) : repr(Repr{s, size}) {} + + Slice &operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; + } + + const T *data() const noexcept { return this->repr.ptr; } + size_t size() const noexcept { return this->repr.len; } + size_t length() const noexcept { return this->repr.len; } + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // At present this class is only used for &[u8] slices. + // Not necessarily ABI compatible with &[u8]. Codegen will translate to + // cxx::rust_slice_u8::RustSlice which matches this layout. + struct Repr { + const T *ptr; + size_t len; + }; + Slice(Repr repr_) noexcept : repr(repr_) {} + explicit operator Repr() noexcept { return this->repr; } + +private: + Repr repr; +}; + +#endif // CXXBRIDGE02_RUST_SLICE + #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING class String final { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 67c34e5f5..146d21b10 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -184,6 +184,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)), ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()), _ => quote!(#var), } @@ -255,6 +256,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), + Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))), _ => None, }) } else { @@ -267,6 +269,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), + Type::SliceRefU8(_) => Some(quote!(#call.as_slice())), _ => None, }) } @@ -375,6 +378,7 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), + Type::SliceRefU8(_) => quote!(#ident.as_slice()), ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), _ => quote!(#ident), } @@ -402,6 +406,7 @@ fn expand_rust_function_shim_impl( _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), + Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))), _ => None, }) .unwrap_or(call); @@ -572,6 +577,7 @@ fn expand_extern_type(ty: &Type) -> TokenStream { _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8), _ => quote!(#ty), } } diff --git a/src/lib.rs b/src/lib.rs index f918b298d..60750fbe7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,6 +367,7 @@ mod gen; mod opaque; mod paths; mod result; +mod rust_sliceu8; mod rust_str; mod rust_string; mod syntax; @@ -384,6 +385,7 @@ pub mod private { pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; + pub use crate::rust_sliceu8::RustSliceU8; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs new file mode 100644 index 000000000..56d37b3d6 --- /dev/null +++ b/src/rust_sliceu8.rs @@ -0,0 +1,26 @@ +use std::mem; +use std::slice; +use std::ptr::NonNull; + +// Not necessarily ABI compatible with &[u8]. Codegen performs the translation. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RustSliceU8 { + pub(crate) ptr: NonNull, + pub(crate) len: usize, +} + +impl RustSliceU8 { + pub fn from(s: &[u8]) -> Self { + RustSliceU8 { + ptr: NonNull::from(s).cast::(), + len: s.len(), + } + } + + pub unsafe fn as_slice<'a>(self) -> &'a [u8] { + slice::from_raw_parts(self.ptr.as_ptr(), self.len) + } +} + +const_assert!(mem::size_of::>() == mem::size_of::()); diff --git a/syntax/check.rs b/syntax/check.rs index 2826a8528..c71295e85 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -245,6 +245,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Slice(_) => "slice".to_owned(), + Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), } diff --git a/syntax/impls.rs b/syntax/impls.rs index 34fb851a4..b04075316 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,4 @@ -use crate::syntax::{ExternFn, Receiver, Ref, Signature, Ty1, Type}; +use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -21,6 +21,8 @@ impl Hash for Type { Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::Fn(t) => t.hash(state), + Type::Slice(t) => t.hash(state), + Type::SliceRefU8(t) => t.hash(state), Type::Void(_) => {} } } @@ -37,6 +39,8 @@ impl PartialEq for Type { (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, + (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, + (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, (Type::Void(_), Type::Void(_)) => true, (_, _) => false, } @@ -106,6 +110,32 @@ impl Hash for Ref { } } +impl Eq for Slice {} + +impl PartialEq for Slice { + fn eq(&self, other: &Slice) -> bool { + let Slice { + bracket: _, + inner, + } = self; + let Slice { + bracket: _, + inner: inner2, + } = other; + inner == inner2 + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + let Slice { + bracket: _, + inner, + } = self; + inner.hash(state); + } +} + impl Eq for Signature {} impl PartialEq for Signature { diff --git a/syntax/mod.rs b/syntax/mod.rs index 0d0328b45..4eaf81e22 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -15,7 +15,7 @@ pub mod types; use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; -use syn::token::{Brace, Paren}; +use syn::token::{Brace, Bracket, Paren}; use syn::{LitStr, Token}; pub use self::atom::Atom; @@ -84,6 +84,8 @@ pub enum Type { Str(Box), Fn(Box), Void(Span), + Slice(Box), + SliceRefU8(Box), } pub struct Ty1 { @@ -99,6 +101,11 @@ pub struct Ref { pub inner: Type, } +pub struct Slice { + pub bracket: Bracket, + pub inner: Type, +} + #[derive(Copy, Clone, PartialEq)] pub enum Lang { Cxx, diff --git a/syntax/parse.rs b/syntax/parse.rs index 8e674bde1..6156a69d1 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,6 +1,6 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Struct, - Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, + Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::{format_ident, quote}; @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, - TypeBareFn, TypePath, TypeReference, + TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -222,10 +222,25 @@ fn parse_type(ty: &RustType) -> Result { RustType::Path(ty) => parse_type_path(ty), RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), + RustType::Slice(ty) => parse_type_slice(ty), _ => Err(Error::new_spanned(ty, "unsupported type")), } } +fn parse_type_slice(ty: &TypeSlice) -> Result { + let inner = parse_type(&ty.elem)?; + let which = match &inner { + Type::Ident(ident) if ident == "u8" => { + Type::Slice + }, + _ => return Err(Error::new_spanned(ty, "unsupported type")) + }; + Ok(which(Box::new(Slice { + bracket: ty.bracket_token, + inner + }))) +} + fn parse_type_reference(ty: &TypeReference) -> Result { let inner = parse_type(&ty.elem)?; let which = match &inner { @@ -236,6 +251,14 @@ fn parse_type_reference(ty: &TypeReference) -> Result { Type::Str } } + Type::Slice(inner2) => { + match &inner2.inner { + Type::Ident(ident) if ident == "u8" => { + Type::SliceRefU8 + } + _ => return Err(Error::new_spanned(ty, "unsupported type")) + } + } _ => Type::Ref, }; Ok(which(Box::new(Ref { diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 51592dd86..df11852ca 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Ref, Signature, Slice, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::Token; @@ -15,7 +15,8 @@ impl ToTokens for Type { ident.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), - Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), + Type::Slice(s) => s.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } @@ -51,6 +52,14 @@ impl ToTokens for Ref { } } +impl ToTokens for Slice { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.bracket.surround(tokens, |tokens| { + self.inner.to_tokens(tokens); + }); + } +} + impl ToTokens for Derive { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { diff --git a/syntax/types.rs b/syntax/types.rs index f65b12fdc..6f3af09d7 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -23,9 +23,10 @@ impl<'a> Types<'a> { fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); match ty { - Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} + Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), + Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { if let Some(ret) = &f.ret { visit(all, ret); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 68651ea70..adcd4fc26 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -24,6 +24,7 @@ pub mod ffi { fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_str(shared: &Shared) -> &str; + fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; @@ -34,6 +35,7 @@ pub mod ffi { fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); + fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); fn c_take_callback(callback: fn(String) -> usize); @@ -44,6 +46,7 @@ pub mod ffi { fn c_try_return_box() -> Result>; 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_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; } @@ -67,6 +70,7 @@ pub mod ffi { fn r_take_ref_r(r: &R); fn r_take_ref_c(c: &C); fn r_take_str(s: &str); + fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); @@ -160,6 +164,11 @@ fn r_take_rust_string(s: String) { assert_eq!(s, "2020"); } +fn r_take_sliceu8(s: &[u8]) { + assert_eq!(s.len(), 5); + assert_eq!(std::str::from_utf8(s).unwrap(), "2020\u{0}"); +} + fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4aad3a2e9..bf5e76ddc 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -9,6 +9,8 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { +const char* SLICE_DATA = "2020"; + C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } @@ -32,6 +34,11 @@ rust::Str c_return_str(const Shared &shared) { return "2020"; } +rust::Slice c_return_sliceu8(const Shared& shared) { + (void)shared; + return rust::Slice((const unsigned char*)SLICE_DATA, 5); +} + rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { @@ -80,6 +87,12 @@ void c_take_str(rust::Str s) { } } +void c_take_sliceu8(rust::Slice s) { + if (std::string((const char*)s.data(), s.size()) == "2020") { + cxx_test_suite_set_correct(); + } +} + void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); @@ -108,6 +121,8 @@ const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } +rust::Slice c_try_return_sliceu8(rust::Slice s) { return s; } + rust::String c_try_return_rust_string() { return c_return_rust_string(); } std::unique_ptr c_try_return_unique_ptr_string() { @@ -146,6 +161,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); + r_take_sliceu8(rust::Slice((const unsigned char*)SLICE_DATA, 5)); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 67136147b..a68be5cce 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,6 +23,7 @@ rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); rust::Str c_return_str(const Shared &shared); +rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); @@ -33,6 +34,7 @@ void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); void c_take_str(rust::Str s); +void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); void c_take_callback(rust::Fn callback); @@ -43,6 +45,7 @@ size_t c_fail_return_primitive(); rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); +rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); diff --git a/tests/test.rs b/tests/test.rs index f8e9ca824..511c52bfc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -29,6 +29,7 @@ fn test_c_return() { ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!("2020", ffi::c_return_str(&shared)); + assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!( "2020", @@ -51,6 +52,7 @@ fn test_c_try_return() { assert_eq!(2020, *ffi::c_try_return_box().unwrap()); assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); + assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap()); assert_eq!("2020", ffi::c_try_return_rust_string().unwrap()); assert_eq!( "2020", @@ -71,6 +73,7 @@ fn test_c_take() { check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); check!(ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); + check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string()
name in Rustname in C++
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd