#[non_exhaustive]pub struct Unboxed<RType: Sized, CType: Sized> { /* private fields */ }Expand description
Unboxed is used to model values that are passed by reference, but where the memory allocation is handled by C. This approach allows the C code to allocate space for the value on the stack or in other structs, often avoiding unnecessary heap allocations.
The two type parameters, RType and CType, must share the same alignment, and RType must not be larger than CType. Functions in this type will cause a runtime panic in debug builds if these requirements are violated.
If the fields of the struct are meant to be accessible to C, RType and CType may be the same type, trivially ensuring the alignment and size requirements are met.
Define your C and Rust types, then a type alias parameterizing Unboxed:
#[repr(C)]
struct ComplexInt {
re: i64,
im: i64,
}
type UnboxedComplexInt = Unboxed<ComplexInt, ComplexInt>;Then call static methods on that type alias.
§Opaque CType
It is not a requirement that the fields of the types match. In fact, a common use of this type is with an “opaque” C type that only contains a “reserved” field large enough to contain the Rust type. There is no constant way to determine the space required for a Rust value, but it is possible to make a conservative guess, possibly leaving some unused space. The suggested C type is represented in Rust as
struct CType([u64; N]);for some N large enough to contain the Rust type on the required platforms. In C, this type would be defined as
struct ctype_t {
_reserved size_t[N];
}for the same N. The types must also have the same alignment; typically using size_t
accomplishes this.
§Constructors
This type provides two functions useful for initialization of a CType given a value of type
RType: to_out_param takes an “output argument” pointing to an uninitialized value, and
initializes it; while return_val returns a struct value that can be used to initialize a C
variable. Both function similarly, so choose the one that makes the most sense for your API.
For example, a constructor which can also return an error may prefer to put the error in the
return value and use to_out_param.
§Safety
C allows uninitialized values, while Rust does not. Be careful in the documentation for the C API to ensure that values are properly initialized before they are used.
Implementations§
Source§impl<RType: Sized, CType: Sized> Unboxed<RType, CType>
impl<RType: Sized, CType: Sized> Unboxed<RType, CType>
Sourcepub unsafe fn take(cval: CType) -> RType
pub unsafe fn take(cval: CType) -> RType
Take a CType and return an owned value.
This approach is uncommon in C APIs. It leaves behind a value in the C allocation which
could be used accidentally, resulting in a use-after-free error. Prefer Unboxed::take_ptr
unless the type is Copy.
§Safety
- cval must be a valid CType value
Sourcepub unsafe fn take_ptr_nonnull(cptr: *mut CType) -> RType
pub unsafe fn take_ptr_nonnull(cptr: *mut CType) -> RType
Take a pointer to a CType and return an owned value.
This is intended for C API functions that take a value by reference (pointer), but still “take ownership” of the value. It leaves behind an invalid value, where any non-padding bytes of the Rust type are zeroed. This makes use-after-free errors in the C code more likely to crash instead of silently working. Which is about as good as it gets in C.
§Safety
Do not pass a pointer to a Rust value to this function:
let rust_value = RustType::take_ptr_nonnull(&mut c_value); // BAD!This creates undefined behavior as Rust will assume c_value is still initialized. Use
Unboxed::take in this situation.
cptrmust not be NULL and must point to a valid CType value (seeUnboxed::take_ptrfor a version allowing NULL)- The memory pointed to by
cptris uninitialized when this function returns.
Examples found in repository?
39pub unsafe extern "C" fn byte_buffer_free(bb: *mut byte_buffer_t) {
40 let bb = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb) };
41 drop(bb); // just to be explicit
42}
43
44/// Checksum a byte_buffer_t's contents by XOR'ing all bytes together.
45#[no_mangle]
46pub unsafe extern "C" fn byte_buffer_checksum(bb: *const byte_buffer_t) -> u8 {
47 unsafe {
48 UnboxedByteBuffer::with_ref_nonnull(bb, |bb| {
49 // ok, not the most exciting "checksum"!
50 bb.0.iter().copied().reduce(|a, b| a ^ b).unwrap_or(0)
51 })
52 }
53}
54
55/// Add a byte to the byte buffer.
56#[no_mangle]
57pub unsafe extern "C" fn byte_buffer_push(bb: *mut byte_buffer_t, b: u8) {
58 unsafe { UnboxedByteBuffer::with_ref_mut_nonnull(bb, |bb| bb.0.push(b)) }
59}
60
61/// Combine two byte buffers, returning a new byte buffer containing the bytes
62/// from both inputs. This function consumes its inputs and they must not be
63/// used after it returns.
64#[no_mangle]
65pub unsafe extern "C" fn byte_buffer_combine(
66 bb1: *mut byte_buffer_t,
67 bb2: *mut byte_buffer_t,
68) -> byte_buffer_t {
69 let mut bb1 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb1) };
70 let bb2 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb2) };
71
72 // modify bb1 in place (but it's not in the caller's location anymore)
73 bb1.0.extend(&bb2.0[..]);
74 unsafe { UnboxedByteBuffer::return_val(bb1) }
75}Sourcepub unsafe fn with_ref_nonnull<T, F: FnOnce(&RType) -> T>(
cptr: *const CType,
f: F,
) -> T
pub unsafe fn with_ref_nonnull<T, F: FnOnce(&RType) -> T>( cptr: *const CType, f: F, ) -> T
Call the contained function with a shared reference to the value.
§Safety
cptrmust not be NULL and must point to a valid CType value (seeUnboxed::with_reffor a version allowing NULL).- no other thread may mutate the value pointed to by
cptruntil the function returns. - ownership of the value remains with the caller.
Sourcepub unsafe fn with_ref_mut_nonnull<T, F: FnOnce(&mut RType) -> T>(
cptr: *mut CType,
f: F,
) -> T
pub unsafe fn with_ref_mut_nonnull<T, F: FnOnce(&mut RType) -> T>( cptr: *mut CType, f: F, ) -> T
Call the contained function with an exclusive reference to the data type.
§Safety
cptrmust not be NULL and must point to a valid CType value (seeUnboxed::with_ref_mutfor a version allowing NULL).- No other thread may access the value pointed to by
cptruntil the function returns. - Ownership of the value remains with the caller.
Sourcepub unsafe fn return_val(rval: RType) -> CType
pub unsafe fn return_val(rval: RType) -> CType
Return a CType containing rval, moving rval in the process.
§Safety
- The caller must ensure that the value is eventually freed.
Examples found in repository?
27pub unsafe extern "C" fn byte_buffer_new() -> byte_buffer_t {
28 unsafe { UnboxedByteBuffer::return_val(ByteBuffer(Vec::new())) }
29}
30
31/// Initialize the given byte_buffer_t to an empty value.
32#[no_mangle]
33pub unsafe extern "C" fn byte_buffer_init(bb: *mut byte_buffer_t) {
34 unsafe { UnboxedByteBuffer::to_out_param_nonnull(ByteBuffer(Vec::new()), bb) }
35}
36
37/// Free a byte_buffer_t.
38#[no_mangle]
39pub unsafe extern "C" fn byte_buffer_free(bb: *mut byte_buffer_t) {
40 let bb = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb) };
41 drop(bb); // just to be explicit
42}
43
44/// Checksum a byte_buffer_t's contents by XOR'ing all bytes together.
45#[no_mangle]
46pub unsafe extern "C" fn byte_buffer_checksum(bb: *const byte_buffer_t) -> u8 {
47 unsafe {
48 UnboxedByteBuffer::with_ref_nonnull(bb, |bb| {
49 // ok, not the most exciting "checksum"!
50 bb.0.iter().copied().reduce(|a, b| a ^ b).unwrap_or(0)
51 })
52 }
53}
54
55/// Add a byte to the byte buffer.
56#[no_mangle]
57pub unsafe extern "C" fn byte_buffer_push(bb: *mut byte_buffer_t, b: u8) {
58 unsafe { UnboxedByteBuffer::with_ref_mut_nonnull(bb, |bb| bb.0.push(b)) }
59}
60
61/// Combine two byte buffers, returning a new byte buffer containing the bytes
62/// from both inputs. This function consumes its inputs and they must not be
63/// used after it returns.
64#[no_mangle]
65pub unsafe extern "C" fn byte_buffer_combine(
66 bb1: *mut byte_buffer_t,
67 bb2: *mut byte_buffer_t,
68) -> byte_buffer_t {
69 let mut bb1 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb1) };
70 let bb2 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb2) };
71
72 // modify bb1 in place (but it's not in the caller's location anymore)
73 bb1.0.extend(&bb2.0[..]);
74 unsafe { UnboxedByteBuffer::return_val(bb1) }
75}Sourcepub unsafe fn to_out_param(rval: RType, arg_out: *mut CType)
pub unsafe fn to_out_param(rval: RType, arg_out: *mut CType)
Initialize the value pointed to arg_out with rval, “moving” rval into the pointer.
If the pointer is NULL, rval is dropped. Use Unboxed::to_out_param_nonnull to
panic in this situation.
§Safety
- The caller must ensure that the value is eventually freed.
- If not NULL,
arg_outmust point to valid, properly aligned memory for CType.
Sourcepub unsafe fn to_out_param_nonnull(rval: RType, arg_out: *mut CType)
pub unsafe fn to_out_param_nonnull(rval: RType, arg_out: *mut CType)
Initialize the value pointed to arg_out with rval, “moving” rval into the pointer.
If the pointer is NULL, this method will panic.
§Safety
- The caller must ensure that the value is eventually freed.
arg_outmust not be NULL and must point to valid, properly aligned memory for CType.
Source§impl<RType: Sized + Default, CType: Sized> Unboxed<RType, CType>
impl<RType: Sized + Default, CType: Sized> Unboxed<RType, CType>
Sourcepub unsafe fn with_ref<T, F: FnOnce(&RType) -> T>(cptr: *const CType, f: F) -> T
pub unsafe fn with_ref<T, F: FnOnce(&RType) -> T>(cptr: *const CType, f: F) -> T
Call the contained function with a shared reference to the value.
If the given pointer is NULL, the contained function is called with a reference to RType’s default value, which is subsequently dropped.
§Safety
- If not NULL,
cptrmust point to a valid CType value. - No other thread may mutate the value pointed to by
cptruntil the function returns. - Ownership of the value remains with the caller.
Sourcepub unsafe fn with_ref_mut<T, F: FnOnce(&mut RType) -> T>(
cptr: *mut CType,
f: F,
) -> T
pub unsafe fn with_ref_mut<T, F: FnOnce(&mut RType) -> T>( cptr: *mut CType, f: F, ) -> T
Call the contained function with an exclusive reference to the data type.
If the given pointer is NULL, the contained function is called with a reference to RType’s default value, which is subsequently dropped.
§Safety
- If not NULL,
cptrmust point to a valid CType value. - No other thread may access the value pointed to by
cptruntil the function returns. - Ownership of the value remains with the caller.
Sourcepub unsafe fn take_ptr(cptr: *mut CType) -> RType
pub unsafe fn take_ptr(cptr: *mut CType) -> RType
Take a pointer to a CType and return an owned value.
This is similar to Unboxed::take_ptr_nonnull, but if given a NULL pointer will return the
default value.
§Safety
- If not NULL,
cptrmust point to a valid CType value. - The memory pointed to by
cptris uninitialized when this function returns.