Trait uniffi::FfiConverter
source · pub unsafe trait FfiConverter: Sized {
type RustType;
type FfiType;
fn lower(obj: Self::RustType) -> Self::FfiType;
fn try_lift(v: Self::FfiType) -> Result<Self::RustType, Error>;
fn write(obj: Self::RustType, buf: &mut Vec<u8, Global>);
fn try_read(buf: &mut &[u8]) -> Result<Self::RustType, Error>;
}
Expand description
Trait defining how to transfer values via the FFI layer.
The FfiConverter
trait defines how to pass values of a particular type back-and-forth over
the uniffi generated FFI layer, both as standalone argument or return values, and as
part of serialized compound data structures.
(This trait is like the IntoFfi
trait from ffi_support
, but local to this crate
so that we can add some alternative implementations for different builtin types,
and so that we can add support for receiving as well as returning).
Safety
This is an unsafe trait (implementing it requires unsafe impl
) because we can’t guarantee
that it’s safe to pass your type out to foreign-language code and back again. Buggy
implementations of this trait might violate some assumptions made by the generated code,
or might not match with the corresponding code in the generated foreign-language bindings.
In general, you should not need to implement this trait by hand, and should instead rely on
implementations generated from your component UDL via the uniffi-bindgen scaffolding
command.
Required Associated Types§
sourcetype RustType
type RustType
The type used in Rust code.
For primitive / standard types, we implement FfiConverter
on the type itself with RustType=Self
.
For user-defined types we create a unit struct and implement it there. This sidesteps
Rust’s orphan rules (ADR-0006).
sourcetype FfiType
type FfiType
The low-level type used for passing values of this type over the FFI.
This must be a C-compatible type (e.g. a numeric primitive, a #[repr(C)]
struct) into
which values of the target rust type can be converted.
For complex data types, we currently recommend using RustBuffer
and serializing
the data for transfer. In theory it could be possible to build a matching
#[repr(C)]
struct for a complex data type and pass that instead, but explicit
serialization is simpler and safer as a starting point.
Required Methods§
sourcefn lower(obj: Self::RustType) -> Self::FfiType
fn lower(obj: Self::RustType) -> Self::FfiType
Lower a rust value of the target type, into an FFI value of type Self::FfiType.
This trait method is used for sending data from rust to the foreign language code, by (hopefully cheaply!) converting it into something that can be passed over the FFI and reconstructed on the other side.
Note that this method takes an owned Self::RustType
; this allows it to transfer ownership
in turn to the foreign language code, e.g. by boxing the value and passing a pointer.
sourcefn try_lift(v: Self::FfiType) -> Result<Self::RustType, Error>
fn try_lift(v: Self::FfiType) -> Result<Self::RustType, Error>
Lift a rust value of the target type, from an FFI value of type Self::FfiType.
This trait method is used for receiving data from the foreign language code in rust, by (hopefully cheaply!) converting it from a low-level FFI value of type Self::FfiType into a high-level rust value of the target type.
Since we cannot statically guarantee that the foreign-language code will send valid values of type Self::FfiType, this method is fallible.
sourcefn write(obj: Self::RustType, buf: &mut Vec<u8, Global>)
fn write(obj: Self::RustType, buf: &mut Vec<u8, Global>)
Write a rust value into a buffer, to send over the FFI in serialized form.
This trait method can be used for sending data from rust to the foreign language code, in cases where we’re not able to use a special-purpose FFI type and must fall back to sending serialized bytes.
Note that this method takes an owned Self::RustType
because it’s transferring ownership
to the foreign language code via the RustBuffer.
sourcefn try_read(buf: &mut &[u8]) -> Result<Self::RustType, Error>
fn try_read(buf: &mut &[u8]) -> Result<Self::RustType, Error>
Read a rust value from a buffer, received over the FFI in serialized form.
This trait method can be used for receiving data from the foreign language code in rust, in cases where we’re not able to use a special-purpose FFI type and must fall back to receiving serialized bytes.
Since we cannot statically guarantee that the foreign-language code will send valid serialized bytes for the target type, this method is fallible.
Note the slightly unusual type here - we want a mutable reference to a slice of bytes, because we want to be able to advance the start of the slice after reading an item from it (but will not mutate the actual contents of the slice).
Implementations on Foreign Types§
source§impl FfiConverter for f64
impl FfiConverter for f64
type RustType = f64
type FfiType = f64
fn lower(obj: <f64 as FfiConverter>::RustType) -> <f64 as FfiConverter>::FfiType
fn try_lift(v: <f64 as FfiConverter>::FfiType) -> Result<f64, Error>
fn write(obj: <f64 as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(buf: &mut &[u8]) -> Result<f64, Error>
source§impl<T> FfiConverter for Arc<T>where
T: Sync + Send,
impl<T> FfiConverter for Arc<T>where
T: Sync + Send,
Support for passing reference-counted shared objects via the FFI.
To avoid dealing with complex lifetime semantics over the FFI, any data passed
by reference must be encapsulated in an Arc
, and must be safe to share
across threads.
source§fn lower(
obj: <Arc<T> as FfiConverter>::RustType
) -> <Arc<T> as FfiConverter>::FfiType
fn lower(
obj: <Arc<T> as FfiConverter>::RustType
) -> <Arc<T> as FfiConverter>::FfiType
When lowering, we have an owned Arc<T>
and we transfer that ownership
to the foreign-language code, “leaking” it out of Rust’s ownership system
as a raw pointer. This works safely because we have unique ownership of self
.
The foreign-language code is responsible for freeing this by calling the
ffi_object_free
FFI function provided by the corresponding UniFFI type.
Safety: when freeing the resulting pointer, the foreign-language code must
call the destructor function specific to the type T
. Calling the destructor
function for other types may lead to undefined behaviour.
source§fn try_lift(
v: <Arc<T> as FfiConverter>::FfiType
) -> Result<<Arc<T> as FfiConverter>::RustType, Error>
fn try_lift(
v: <Arc<T> as FfiConverter>::FfiType
) -> Result<<Arc<T> as FfiConverter>::RustType, Error>
When lifting, we receive a “borrow” of the Arc<T>
that is owned by
the foreign-language code, and make a clone of it for our own use.
Safety: the provided value must be a pointer previously obtained by calling
the lower()
or write()
method of this impl.
source§fn write(obj: <Arc<T> as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn write(obj: <Arc<T> as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
When writing as a field of a complex structure, make a clone and transfer ownership
of it to the foreign-language code by writing its pointer into the buffer.
The foreign-language code is responsible for freeing this by calling the
ffi_object_free
FFI function provided by the corresponding UniFFI type.
Safety: when freeing the resulting pointer, the foreign-language code must
call the destructor function specific to the type T
. Calling the destructor
function for other types may lead to undefined behaviour.
source§fn try_read(
buf: &mut &[u8]
) -> Result<<Arc<T> as FfiConverter>::RustType, Error>
fn try_read(
buf: &mut &[u8]
) -> Result<<Arc<T> as FfiConverter>::RustType, Error>
When reading as a field of a complex structure, we receive a “borrow” of the Arc<T>
that is owned by the foreign-language code, and make a clone for our own use.
Safety: the buffer must contain a pointer previously obtained by calling
the lower()
or write()
method of this impl.
type RustType = Arc<T>
type FfiType = *const c_void
source§impl FfiConverter for u32
impl FfiConverter for u32
type RustType = u32
type FfiType = u32
fn lower(obj: <u32 as FfiConverter>::RustType) -> <u32 as FfiConverter>::FfiType
fn try_lift(v: <u32 as FfiConverter>::FfiType) -> Result<u32, Error>
fn write(obj: <u32 as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(buf: &mut &[u8]) -> Result<u32, Error>
source§impl FfiConverter for u16
impl FfiConverter for u16
type RustType = u16
type FfiType = u16
fn lower(obj: <u16 as FfiConverter>::RustType) -> <u16 as FfiConverter>::FfiType
fn try_lift(v: <u16 as FfiConverter>::FfiType) -> Result<u16, Error>
fn write(obj: <u16 as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(buf: &mut &[u8]) -> Result<u16, Error>
source§impl FfiConverter for String
impl FfiConverter for String
Support for passing Strings via the FFI.
Unlike many other implementations of FfiConverter
, this passes a struct containing
a raw pointer rather than copying the data from one side to the other. This is a
safety hazard, but turns out to be pretty nice for useability. This struct
must be a valid RustBuffer
and it must contain valid utf-8 data (in other
words, it must be a Vec<u8>
suitable for use as an actual rust String
).
When serialized in a buffer, strings are represented as a i32 byte length followed by utf8-encoded bytes. (It’s a signed integer because unsigned types are currently experimental in Kotlin).
type RustType = String
type FfiType = RustBuffer
fn lower(
obj: <String as FfiConverter>::RustType
) -> <String as FfiConverter>::FfiType
fn try_lift(
v: <String as FfiConverter>::FfiType
) -> Result<<String as FfiConverter>::RustType, Error>
fn write(obj: <String as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(
buf: &mut &[u8]
) -> Result<<String as FfiConverter>::RustType, Error>
source§impl FfiConverter for i16
impl FfiConverter for i16
type RustType = i16
type FfiType = i16
fn lower(obj: <i16 as FfiConverter>::RustType) -> <i16 as FfiConverter>::FfiType
fn try_lift(v: <i16 as FfiConverter>::FfiType) -> Result<i16, Error>
fn write(obj: <i16 as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(buf: &mut &[u8]) -> Result<i16, Error>
source§impl FfiConverter for i32
impl FfiConverter for i32
type RustType = i32
type FfiType = i32
fn lower(obj: <i32 as FfiConverter>::RustType) -> <i32 as FfiConverter>::FfiType
fn try_lift(v: <i32 as FfiConverter>::FfiType) -> Result<i32, Error>
fn write(obj: <i32 as FfiConverter>::RustType, buf: &mut Vec<u8, Global>)
fn try_read(buf: &mut &[u8]) -> Result<i32, Error>
source§impl FfiConverter for bool
impl FfiConverter for bool
Support for passing boolean values via the FFI.
Booleans are passed as an i8
in order to avoid problems with handling
C-compatible boolean values on JVM-based languages.