Skip to main content

ffi_convert/
conversions.rs

1use std::ffi::NulError;
2use std::mem::MaybeUninit;
3use std::str::Utf8Error;
4
5use thiserror::Error;
6
7/// Error returned by [`CReprOf::c_repr_of`].
8#[derive(Error, Debug)]
9pub enum CReprOfError {
10    /// A Rust [`String`] contained an interior `NUL` byte and therefore could
11    /// not be converted to a C string.
12    #[error("A string contains a nul bit")]
13    StringContainsNullBit(#[from] NulError),
14    /// Custom error returned by a manual or overridden implementation.
15    #[error("An error occurred during conversion to C repr; {}", .0)]
16    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
17}
18
19/// Consuming conversion **from** an idiomatic Rust value **to** its
20/// `#[repr(C)]` mirror.
21///
22/// Implementing `CReprOf<U>` for `T` states that `T` is a C-compatible layout
23/// of the Rust value `U` and that a `T` can be built from a `U`. The resulting
24/// `T` owns any heap memory it allocates, and that memory is reclaimed by the
25/// corresponding [`CDrop`] implementation.
26///
27/// see  [Deriving the traits](crate#deriving-the-traits).
28pub trait CReprOf<T>: Sized + CDrop {
29    /// Consume `input` and return its C-compatible representation.
30    fn c_repr_of(input: T) -> Result<Self, CReprOfError>;
31}
32
33/// Error returned by [`CDrop::do_drop`].
34#[derive(Error, Debug)]
35pub enum CDropError {
36    /// A non-nullable pointer field was found to be null while dropping.
37    #[error("unexpected null pointer")]
38    NullPointer(#[from] UnexpectedNullPointerError),
39    /// Custom error returned by a manual implementation.
40    #[error("An error occurred while dropping C struct: {}", .0)]
41    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
42}
43
44/// Releases heap memory referenced by a C-compatible value behind raw pointer
45/// fields (typically data that was moved into a `Box` and leaked via
46/// [`Box::into_raw`]).
47///
48/// By default, [`#[derive(CDrop)]`](ffi_convert_derive::CDrop) emits both a
49/// [`CDrop`] impl and a matching [`Drop`] impl that calls
50/// [`do_drop`](CDrop::do_drop), so dropping the value through Rust's normal
51/// path releases its pointer fields. `#[no_drop_impl]` suppresses only the
52/// [`Drop`] impl; in that case a handwritten [`Drop`] must call `do_drop`
53/// itself, otherwise the pointer fields are leaked.
54///
55/// see [Deriving the traits](crate#deriving-the-traits).
56pub trait CDrop {
57    /// Release any Rust-owned memory referenced by `self`. The derived
58    /// [`Drop`] impl calls this and discards the result, so errors raised
59    /// from a normal drop are not observed.
60    fn do_drop(&mut self) -> Result<(), CDropError>;
61}
62
63/// Error returned by [`AsRust::as_rust`].
64#[derive(Error, Debug)]
65pub enum AsRustError {
66    /// A non-nullable pointer field was null.
67    #[error("unexpected null pointer")]
68    NullPointer(#[from] UnexpectedNullPointerError),
69    /// A C string field was not valid UTF-8.
70    #[error("could not convert string as it is not UTF-8: {}", .0)]
71    Utf8Error(#[from] Utf8Error),
72    /// Custom error returned by a manual implementation.
73    #[error("An error occurred during conversion to Rust: {}", .0)]
74    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
75}
76
77/// Non-consuming conversion **from** a `#[repr(C)]` value **back** to an
78/// owned, idiomatic Rust value.
79///
80/// `AsRust<U>` takes `&self` and returns a freshly-allocated `U`, copying data
81/// out of any pointer field by borrowing through [`RawBorrow`]. The original
82/// C-compatible value is left untouched and its allocations are not freed;
83/// releasing them is the caller's responsibility.
84///
85/// This is the recommended entry point for values handed to Rust by C — see
86/// the crate-level [Philosophy](crate#philosophy).
87pub trait AsRust<T> {
88    /// Return a freshly-allocated Rust value equivalent to `self`.
89    fn as_rust(&self) -> Result<T, AsRustError>;
90}
91
92/// Returned when a raw pointer was expected to be non-null but was null.
93#[derive(Error, Debug)]
94#[error("Could not use raw pointer: unexpected null pointer")]
95pub struct UnexpectedNullPointerError;
96
97/// Moves a Rust value onto the heap and exposes it as a raw pointer suitable
98/// for crossing an FFI boundary, then takes it back on the return trip.
99///
100/// The default impls box the value and leak it via [`Box::into_raw`]. Each
101/// pointer produced by `into_raw_pointer` must eventually be passed to
102/// [`from_raw_pointer`](RawPointerConverter::from_raw_pointer) or
103/// [`drop_raw_pointer`](RawPointerConverter::drop_raw_pointer); otherwise the
104/// allocation is leaked. To read the value behind a pointer without taking
105/// ownership (e.g. when the C caller retains ownership), use [`RawBorrow`]
106/// — see the crate-level [Philosophy](crate#philosophy).
107///
108/// The `from_raw_pointer` family is `unsafe` because the compiler cannot
109/// verify that the pointer originated from `into_raw_pointer`. Passing the
110/// same pointer twice frees the same allocation twice.
111pub trait RawPointerConverter<T>: Sized {
112    /// Leak the value behind a raw pointer. Pair with [`Self::from_raw_pointer`]
113    /// or [`Self::drop_raw_pointer`] to release the allocation.
114    fn into_raw_pointer(self) -> *const T;
115    /// Leak the value behind a mutable raw pointer. Pair with
116    /// [`Self::from_raw_pointer_mut`] or [`Self::drop_raw_pointer_mut`] to
117    /// release the allocation.
118    fn into_raw_pointer_mut(self) -> *mut T;
119    /// Take back ownership of a raw pointer previously produced by
120    /// [`Self::into_raw_pointer`]. Returns [`UnexpectedNullPointerError`] if
121    /// `input` is null.
122    /// # Safety
123    /// `input` must have been produced by [`Self::into_raw_pointer`] and must
124    /// not be used afterwards. Passing the same pointer twice frees the same
125    /// allocation twice.
126    unsafe fn from_raw_pointer(input: *const T) -> Result<Self, UnexpectedNullPointerError>;
127    /// Take back ownership of a raw pointer previously produced by
128    /// [`Self::into_raw_pointer_mut`]. Returns [`UnexpectedNullPointerError`]
129    /// if `input` is null.
130    /// # Safety
131    /// `input` must have been produced by [`Self::into_raw_pointer_mut`] and
132    /// must not be used afterwards. Passing the same pointer twice frees the
133    /// same allocation twice.
134    unsafe fn from_raw_pointer_mut(input: *mut T) -> Result<Self, UnexpectedNullPointerError>;
135
136    /// Take back ownership of a pointer produced by [`Self::into_raw_pointer`]
137    /// and drop the value.
138    /// # Safety
139    /// Same requirements as [`Self::from_raw_pointer`].
140    unsafe fn drop_raw_pointer(input: *const T) -> Result<(), UnexpectedNullPointerError> {
141        unsafe { Self::from_raw_pointer(input) }.map(|_| ())
142    }
143
144    /// Take back ownership of a pointer produced by [`Self::into_raw_pointer_mut`]
145    /// and drop the value.
146    /// # Safety
147    /// Same requirements as [`Self::from_raw_pointer_mut`].
148    unsafe fn drop_raw_pointer_mut(input: *mut T) -> Result<(), UnexpectedNullPointerError> {
149        unsafe { Self::from_raw_pointer_mut(input) }.map(|_| ())
150    }
151}
152
153#[doc(hidden)]
154pub fn convert_into_raw_pointer<T>(pointee: T) -> *const T {
155    Box::into_raw(Box::new(pointee)) as _
156}
157
158#[doc(hidden)]
159pub fn convert_into_raw_pointer_mut<T>(pointee: T) -> *mut T {
160    Box::into_raw(Box::new(pointee))
161}
162
163#[doc(hidden)]
164pub unsafe fn take_back_from_raw_pointer<T>(
165    input: *const T,
166) -> Result<T, UnexpectedNullPointerError> {
167    unsafe { take_back_from_raw_pointer_mut(input as _) }
168}
169
170#[doc(hidden)]
171pub unsafe fn take_back_from_raw_pointer_mut<T>(
172    input: *mut T,
173) -> Result<T, UnexpectedNullPointerError> {
174    if input.is_null() {
175        Err(UnexpectedNullPointerError)
176    } else {
177        Ok(*unsafe { Box::from_raw(input) })
178    }
179}
180
181/// Turn a `*const T` into a borrowed `&T` without taking ownership.
182///
183/// Use this when the pointer was handed to you by C and the C side retains
184/// ownership of the allocation — see the crate-level
185/// [Philosophy](crate#philosophy). A blanket impl `impl<T> RawBorrow<T> for T`
186/// covers every type; [`std::ffi::CStr`] additionally implements
187/// `RawBorrow<std::ffi::c_char>`.
188pub trait RawBorrow<T> {
189    /// Borrow the value behind `input`, or return
190    /// [`UnexpectedNullPointerError`] if it is null.
191    ///
192    /// # Safety
193    /// Thin wrapper around `<*const T>::as_ref` with the same requirements:
194    /// `input` must point to a valid, properly aligned `T` that lives for at
195    /// least `'a`.
196    unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError>;
197}
198
199/// Mutable counterpart of [`RawBorrow`].
200pub trait RawBorrowMut<T> {
201    /// Borrow the value behind `input` mutably, or return
202    /// [`UnexpectedNullPointerError`] if it is null.
203    ///
204    /// # Safety
205    /// Thin wrapper around `<*mut T>::as_mut` with the same requirements.
206    unsafe fn raw_borrow_mut<'a>(input: *mut T)
207    -> Result<&'a mut Self, UnexpectedNullPointerError>;
208}
209
210impl<T> RawBorrow<T> for T {
211    unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError> {
212        unsafe { input.as_ref() }.ok_or(UnexpectedNullPointerError)
213    }
214}
215
216impl<T> RawBorrowMut<T> for T {
217    unsafe fn raw_borrow_mut<'a>(
218        input: *mut T,
219    ) -> Result<&'a mut Self, UnexpectedNullPointerError> {
220        unsafe { input.as_mut() }.ok_or(UnexpectedNullPointerError)
221    }
222}
223
224impl RawPointerConverter<std::ffi::c_void> for std::ffi::CString {
225    fn into_raw_pointer(self) -> *const std::ffi::c_void {
226        self.into_raw() as _
227    }
228
229    fn into_raw_pointer_mut(self) -> *mut std::ffi::c_void {
230        self.into_raw() as _
231    }
232
233    unsafe fn from_raw_pointer(
234        input: *const std::ffi::c_void,
235    ) -> Result<Self, UnexpectedNullPointerError> {
236        unsafe { Self::from_raw_pointer_mut(input as *mut std::ffi::c_void) }
237    }
238
239    unsafe fn from_raw_pointer_mut(
240        input: *mut std::ffi::c_void,
241    ) -> Result<Self, UnexpectedNullPointerError> {
242        if input.is_null() {
243            Err(UnexpectedNullPointerError)
244        } else {
245            Ok(unsafe { std::ffi::CString::from_raw(input as *mut std::ffi::c_char) })
246        }
247    }
248}
249
250impl RawPointerConverter<std::ffi::c_char> for std::ffi::CString {
251    fn into_raw_pointer(self) -> *const std::ffi::c_char {
252        self.into_raw() as _
253    }
254
255    fn into_raw_pointer_mut(self) -> *mut std::ffi::c_char {
256        self.into_raw()
257    }
258
259    unsafe fn from_raw_pointer(
260        input: *const std::ffi::c_char,
261    ) -> Result<Self, UnexpectedNullPointerError> {
262        unsafe { Self::from_raw_pointer_mut(input as *mut std::ffi::c_char) }
263    }
264
265    unsafe fn from_raw_pointer_mut(
266        input: *mut std::ffi::c_char,
267    ) -> Result<Self, UnexpectedNullPointerError> {
268        if input.is_null() {
269            Err(UnexpectedNullPointerError)
270        } else {
271            Ok(unsafe { std::ffi::CString::from_raw(input as *mut std::ffi::c_char) })
272        }
273    }
274}
275
276impl RawBorrow<std::ffi::c_char> for std::ffi::CStr {
277    unsafe fn raw_borrow<'a>(
278        input: *const std::ffi::c_char,
279    ) -> Result<&'a Self, UnexpectedNullPointerError> {
280        if input.is_null() {
281            Err(UnexpectedNullPointerError)
282        } else {
283            Ok(unsafe { Self::from_ptr(input) })
284        }
285    }
286}
287
288macro_rules! impl_noop_c_drop_for {
289    ($typ:ty) => {
290        impl CDrop for $typ {
291            fn do_drop(&mut self) -> Result<(), CDropError> {
292                Ok(())
293            }
294        }
295    };
296}
297
298impl_noop_c_drop_for!(usize);
299impl_noop_c_drop_for!(i8);
300impl_noop_c_drop_for!(u8);
301impl_noop_c_drop_for!(i16);
302impl_noop_c_drop_for!(u16);
303impl_noop_c_drop_for!(i32);
304impl_noop_c_drop_for!(u32);
305impl_noop_c_drop_for!(i64);
306impl_noop_c_drop_for!(u64);
307impl_noop_c_drop_for!(f32);
308impl_noop_c_drop_for!(f64);
309impl_noop_c_drop_for!(bool);
310impl_noop_c_drop_for!(std::ffi::CString);
311
312macro_rules! impl_c_repr_of_for {
313    ($typ:ty) => {
314        impl CReprOf<$typ> for $typ {
315            fn c_repr_of(input: $typ) -> Result<$typ, CReprOfError> {
316                Ok(input)
317            }
318        }
319    };
320
321    ($from_typ:ty, $to_typ:ty) => {
322        impl CReprOf<$from_typ> for $to_typ {
323            fn c_repr_of(input: $from_typ) -> Result<$to_typ, CReprOfError> {
324                Ok(input as $to_typ)
325            }
326        }
327    };
328}
329
330impl_c_repr_of_for!(usize);
331impl_c_repr_of_for!(i8);
332impl_c_repr_of_for!(u8);
333impl_c_repr_of_for!(i16);
334impl_c_repr_of_for!(u16);
335impl_c_repr_of_for!(i32);
336impl_c_repr_of_for!(u32);
337impl_c_repr_of_for!(i64);
338impl_c_repr_of_for!(u64);
339impl_c_repr_of_for!(f32);
340impl_c_repr_of_for!(f64);
341impl_c_repr_of_for!(bool);
342
343impl_c_repr_of_for!(usize, i32);
344
345impl CReprOf<String> for std::ffi::CString {
346    fn c_repr_of(input: String) -> Result<Self, CReprOfError> {
347        Ok(std::ffi::CString::new(input)?)
348    }
349}
350
351macro_rules! impl_as_rust_for {
352    ($typ:ty) => {
353        impl AsRust<$typ> for $typ {
354            fn as_rust(&self) -> Result<$typ, AsRustError> {
355                Ok(*self)
356            }
357        }
358    };
359
360    ($from_typ:ty, $to_typ:ty) => {
361        impl AsRust<$to_typ> for $from_typ {
362            fn as_rust(&self) -> Result<$to_typ, AsRustError> {
363                Ok(*self as $to_typ)
364            }
365        }
366    };
367}
368
369impl_as_rust_for!(usize);
370impl_as_rust_for!(i8);
371impl_as_rust_for!(u8);
372impl_as_rust_for!(i16);
373impl_as_rust_for!(u16);
374impl_as_rust_for!(i32);
375impl_as_rust_for!(u32);
376impl_as_rust_for!(i64);
377impl_as_rust_for!(u64);
378impl_as_rust_for!(f32);
379impl_as_rust_for!(f64);
380impl_as_rust_for!(bool);
381
382impl_as_rust_for!(i32, usize);
383
384impl AsRust<String> for std::ffi::CStr {
385    fn as_rust(&self) -> Result<String, AsRustError> {
386        self.to_str().map(|s| s.to_owned()).map_err(|e| e.into())
387    }
388}
389
390macro_rules! impl_rawpointerconverter_for {
391    ($typ:ty) => {
392        impl RawPointerConverter<$typ> for $typ {
393            fn into_raw_pointer(self) -> *const $typ {
394                convert_into_raw_pointer(self)
395            }
396            fn into_raw_pointer_mut(self) -> *mut $typ {
397                convert_into_raw_pointer_mut(self)
398            }
399            unsafe fn from_raw_pointer(
400                input: *const $typ,
401            ) -> Result<Self, UnexpectedNullPointerError> {
402                unsafe { take_back_from_raw_pointer(input) }
403            }
404            unsafe fn from_raw_pointer_mut(
405                input: *mut $typ,
406            ) -> Result<Self, UnexpectedNullPointerError> {
407                unsafe { take_back_from_raw_pointer_mut(input) }
408            }
409        }
410    };
411}
412
413impl_rawpointerconverter_for!(usize);
414impl_rawpointerconverter_for!(i16);
415impl_rawpointerconverter_for!(u16);
416impl_rawpointerconverter_for!(i32);
417impl_rawpointerconverter_for!(u32);
418impl_rawpointerconverter_for!(i64);
419impl_rawpointerconverter_for!(u64);
420impl_rawpointerconverter_for!(f32);
421impl_rawpointerconverter_for!(f64);
422impl_rawpointerconverter_for!(bool);
423
424impl<U, T: CReprOf<U>, const N: usize> CReprOf<[U; N]> for [T; N]
425where
426    [T; N]: CDrop,
427{
428    fn c_repr_of(values: [U; N]) -> Result<[T; N], CReprOfError> {
429        let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
430
431        for (n, value) in values.into_iter().enumerate() {
432            let item = &mut array[n];
433
434            match T::c_repr_of(value) {
435                Ok(value) => {
436                    item.write(value);
437                }
438                Err(err) => {
439                    // Drop initialized items
440                    for item in &mut array[0..n] {
441                        // SAFETY: `item` is certain to be initialized
442                        unsafe {
443                            let _ = item.assume_init_mut().do_drop();
444                        }
445                    }
446
447                    return Err(err);
448                }
449            }
450        }
451
452        // SAFETY: `array` is certain to be initialized
453        let array = unsafe {
454            // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/96097
455            (&raw const array).cast::<[T; N]>().read()
456        };
457        Ok(array)
458    }
459}
460
461impl<T: CDrop, const N: usize> CDrop for [T; N] {
462    fn do_drop(&mut self) -> Result<(), CDropError> {
463        let mut result = Ok(());
464
465        for value in self {
466            if let Err(err) = value.do_drop()
467                && result.is_ok()
468            {
469                result = Err(err);
470            }
471        }
472
473        result
474    }
475}
476
477impl<U: AsRust<T>, T, const N: usize> AsRust<[T; N]> for [U; N] {
478    fn as_rust(&self) -> Result<[T; N], AsRustError> {
479        let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
480
481        for (n, value) in self.iter().enumerate() {
482            let item = &mut array[n];
483
484            match value.as_rust() {
485                Ok(value) => {
486                    item.write(value);
487                }
488                Err(err) => {
489                    // Drop initialized items
490                    for item in &mut array[0..n] {
491                        // SAFETY: `item` is certain to be initialized
492                        unsafe {
493                            item.assume_init_drop();
494                        }
495                    }
496
497                    return Err(err);
498                }
499            }
500        }
501
502        // SAFETY: `array` is certain to be initialized
503        let array = unsafe {
504            // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/96097
505            (&raw const array).cast::<[T; N]>().read()
506        };
507        Ok(array)
508    }
509}