uniffi_core/
ffi_converter_impls.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/// This module contains builtin `FFIConverter` implementations.  These cover:
6///   - Simple privitive types: u8, i32, String, Arc<T>, etc
7///   - Composite types: Vec<T>, Option<T>, etc.
8///   - SystemTime and Duration, which maybe shouldn`t be built-in, but have been historically and
9///     we want to continue to support them for now.
10///
11/// As described in
12/// https://mozilla.github.io/uniffi-rs/internals/lifting_and_lowering.html#code-generation-and-the-fficonverter-trait,
13/// we use the following system:
14///
15///   - Each UniFFIed crate defines a unit struct named `UniFfiTag`
16///   - We define an `impl FFIConverter<UniFfiTag> for Type` for each type that we want to pass
17///     across the FFI.
18///   - When generating the code, we use the `<T as ::uniffi::FFIConverter<crate::UniFfiTag>>` impl
19///     to lift/lower/serialize types for a crate.
20///
21/// This crate needs to implement `FFIConverter<UT>` on `UniFfiTag` instances for all UniFFI
22/// consumer crates.  To do this, it defines blanket impls like `impl<UT> FFIConverter<UT> for u8`.
23/// "UT" means an arbitrary `UniFfiTag` type.
24use crate::{
25    check_remaining, derive_ffi_traits, ffi_converter_rust_buffer_lift_and_lower, metadata,
26    ConvertError, FfiConverter, Lift, LiftRef, LiftReturn, Lower, LowerError, LowerReturn,
27    MetadataBuffer, Result, RustBuffer, RustCallError, TypeId, UnexpectedUniFFICallbackError,
28};
29use anyhow::bail;
30use bytes::buf::{Buf, BufMut};
31use std::{
32    collections::HashMap,
33    convert::TryFrom,
34    fmt::{Debug, Display},
35    sync::Arc,
36    time::{Duration, SystemTime},
37};
38
39/// Blanket implementation of `FfiConverter` for numeric primitives.
40///
41/// Numeric primitives have a straightforward mapping into C-compatible numeric types,
42/// sice they are themselves a C-compatible numeric type!
43macro_rules! impl_ffi_converter_for_num_primitive {
44    ($T:ty, $type_code:expr, $get:ident, $put:ident) => {
45        unsafe impl<UT> FfiConverter<UT> for $T {
46            type FfiType = $T;
47
48            fn lower(obj: $T) -> Self::FfiType {
49                obj
50            }
51
52            fn try_lift(v: Self::FfiType) -> Result<$T> {
53                Ok(v)
54            }
55
56            fn write(obj: $T, buf: &mut Vec<u8>) {
57                buf.$put(obj);
58            }
59
60            fn try_read(buf: &mut &[u8]) -> Result<$T> {
61                check_remaining(buf, std::mem::size_of::<$T>())?;
62                Ok(buf.$get())
63            }
64
65            const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code($type_code);
66        }
67    };
68}
69
70impl_ffi_converter_for_num_primitive!(u8, metadata::codes::TYPE_U8, get_u8, put_u8);
71impl_ffi_converter_for_num_primitive!(i8, metadata::codes::TYPE_I8, get_i8, put_i8);
72impl_ffi_converter_for_num_primitive!(u16, metadata::codes::TYPE_U16, get_u16, put_u16);
73impl_ffi_converter_for_num_primitive!(i16, metadata::codes::TYPE_I16, get_i16, put_i16);
74impl_ffi_converter_for_num_primitive!(u32, metadata::codes::TYPE_U32, get_u32, put_u32);
75impl_ffi_converter_for_num_primitive!(i32, metadata::codes::TYPE_I32, get_i32, put_i32);
76impl_ffi_converter_for_num_primitive!(u64, metadata::codes::TYPE_U64, get_u64, put_u64);
77impl_ffi_converter_for_num_primitive!(i64, metadata::codes::TYPE_I64, get_i64, put_i64);
78impl_ffi_converter_for_num_primitive!(f32, metadata::codes::TYPE_F32, get_f32, put_f32);
79impl_ffi_converter_for_num_primitive!(f64, metadata::codes::TYPE_F64, get_f64, put_f64);
80
81/// Support for passing boolean values via the FFI.
82///
83/// Booleans are passed as an `i8` in order to avoid problems with handling
84/// C-compatible boolean values on JVM-based languages.
85unsafe impl<UT> FfiConverter<UT> for bool {
86    type FfiType = i8;
87
88    fn lower(obj: bool) -> Self::FfiType {
89        i8::from(obj)
90    }
91
92    fn try_lift(v: Self::FfiType) -> Result<bool> {
93        Ok(match v {
94            0 => false,
95            1 => true,
96            _ => bail!("unexpected byte for Boolean"),
97        })
98    }
99
100    fn write(obj: bool, buf: &mut Vec<u8>) {
101        buf.put_i8(<Self as FfiConverter<UT>>::lower(obj));
102    }
103
104    fn try_read(buf: &mut &[u8]) -> Result<bool> {
105        check_remaining(buf, 1)?;
106        <Self as FfiConverter<UT>>::try_lift(buf.get_i8())
107    }
108
109    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_BOOL);
110}
111
112/// Support for passing Strings via the FFI.
113///
114/// Unlike many other implementations of `FfiConverter`, this passes a struct containing
115/// a raw pointer rather than copying the data from one side to the other. This is a
116/// safety hazard, but turns out to be pretty nice for useability. This struct
117/// *must* be a valid `RustBuffer` and it *must* contain valid utf-8 data (in other
118/// words, it *must* be a `Vec<u8>` suitable for use as an actual rust `String`).
119///
120/// When serialized in a buffer, strings are represented as a i32 byte length
121/// followed by utf8-encoded bytes. (It's a signed integer because unsigned types are
122/// currently experimental in Kotlin).
123unsafe impl<UT> FfiConverter<UT> for String {
124    type FfiType = RustBuffer;
125
126    // This returns a struct with a raw pointer to the underlying bytes, so it's very
127    // important that it consume ownership of the String, which is relinquished to the
128    // foreign language code (and can be restored by it passing the pointer back).
129    fn lower(obj: String) -> Self::FfiType {
130        RustBuffer::from_vec(obj.into_bytes())
131    }
132
133    // The argument here *must* be a uniquely-owned `RustBuffer` previously obtained
134    // from `lower` above, and hence must be the bytes of a valid rust string.
135    fn try_lift(v: Self::FfiType) -> Result<String> {
136        let v = v.destroy_into_vec();
137        // This turns the buffer back into a `String` without copying the data
138        // and without re-checking it for validity of the utf8. If the `RustBuffer`
139        // came from a valid String then there's no point in re-checking the utf8,
140        // and if it didn't then bad things are probably going to happen regardless
141        // of whether we check for valid utf8 data or not.
142        Ok(unsafe { String::from_utf8_unchecked(v) })
143    }
144
145    fn write(obj: String, buf: &mut Vec<u8>) {
146        // N.B. `len()` gives us the length in bytes, not in chars or graphemes.
147        // TODO: it would be nice not to panic here.
148        let len = i32::try_from(obj.len()).unwrap();
149        buf.put_i32(len); // We limit strings to u32::MAX bytes
150        buf.put(obj.as_bytes());
151    }
152
153    fn try_read(buf: &mut &[u8]) -> Result<String> {
154        check_remaining(buf, 4)?;
155        let len = usize::try_from(buf.get_i32())?;
156        check_remaining(buf, len)?;
157        // N.B: In the general case `Buf::chunk()` may return partial data.
158        // But in the specific case of `<&[u8] as Buf>` it returns the full slice,
159        // so there is no risk of having less than `len` bytes available here.
160        let bytes = &buf.chunk()[..len];
161        let res = String::from_utf8(bytes.to_vec())?;
162        buf.advance(len);
163        Ok(res)
164    }
165
166    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_STRING);
167}
168
169/// Support for passing timestamp values via the FFI.
170///
171/// Timestamps values are currently always passed by serializing to a buffer.
172///
173/// Timestamps are represented on the buffer by an i64 that indicates the
174/// direction and the magnitude in seconds of the offset from epoch, and a
175/// u32 that indicates the nanosecond portion of the offset magnitude. The
176/// nanosecond portion is expected to be between 0 and 999,999,999.
177///
178/// To build an epoch offset the absolute value of the seconds portion of the
179/// offset should be combined with the nanosecond portion. This is because
180/// the sign of the seconds portion represents the direction of the offset
181/// overall. The sign of the seconds portion can then be used to determine
182/// if the total offset should be added to or subtracted from the unix epoch.
183unsafe impl<UT> FfiConverter<UT> for SystemTime {
184    ffi_converter_rust_buffer_lift_and_lower!(UT);
185
186    fn write(obj: SystemTime, buf: &mut Vec<u8>) {
187        let mut sign = 1;
188        let epoch_offset = obj
189            .duration_since(SystemTime::UNIX_EPOCH)
190            .unwrap_or_else(|error| {
191                sign = -1;
192                error.duration()
193            });
194        // This panic should never happen as SystemTime typically stores seconds as i64
195        let seconds = sign
196            * i64::try_from(epoch_offset.as_secs())
197                .expect("SystemTime overflow, seconds greater than i64::MAX");
198
199        buf.put_i64(seconds);
200        buf.put_u32(epoch_offset.subsec_nanos());
201    }
202
203    fn try_read(buf: &mut &[u8]) -> Result<SystemTime> {
204        check_remaining(buf, 12)?;
205        let seconds = buf.get_i64();
206        let nanos = buf.get_u32();
207        let epoch_offset = Duration::new(seconds.wrapping_abs() as u64, nanos);
208
209        if seconds >= 0 {
210            Ok(SystemTime::UNIX_EPOCH + epoch_offset)
211        } else {
212            Ok(SystemTime::UNIX_EPOCH - epoch_offset)
213        }
214    }
215
216    const TYPE_ID_META: MetadataBuffer =
217        MetadataBuffer::from_code(metadata::codes::TYPE_SYSTEM_TIME);
218}
219
220/// Support for passing duration values via the FFI.
221///
222/// Duration values are currently always passed by serializing to a buffer.
223///
224/// Durations are represented on the buffer by a u64 that indicates the
225/// magnitude in seconds, and a u32 that indicates the nanosecond portion
226/// of the magnitude. The nanosecond portion is expected to be between 0
227/// and 999,999,999.
228unsafe impl<UT> FfiConverter<UT> for Duration {
229    ffi_converter_rust_buffer_lift_and_lower!(UT);
230
231    fn write(obj: Duration, buf: &mut Vec<u8>) {
232        buf.put_u64(obj.as_secs());
233        buf.put_u32(obj.subsec_nanos());
234    }
235
236    fn try_read(buf: &mut &[u8]) -> Result<Duration> {
237        check_remaining(buf, 12)?;
238        Ok(Duration::new(buf.get_u64(), buf.get_u32()))
239    }
240
241    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_DURATION);
242}
243
244// Support for passing optional values via the FFI.
245//
246// Optional values are currently always passed by serializing to a buffer.
247// We write either a zero byte for `None`, or a one byte followed by the containing
248// item for `Some`.
249//
250// In future we could do the same optimization as rust uses internally, where the
251// `None` option is represented as a null pointer and the `Some` as a valid pointer,
252// but that seems more fiddly and less safe in the short term, so it can wait.
253
254unsafe impl<UT, T: Lower<UT>> Lower<UT> for Option<T> {
255    type FfiType = RustBuffer;
256
257    fn write(obj: Option<T>, buf: &mut Vec<u8>) {
258        match obj {
259            None => buf.put_i8(0),
260            Some(v) => {
261                buf.put_i8(1);
262                T::write(v, buf);
263            }
264        }
265    }
266
267    fn lower(obj: Option<T>) -> RustBuffer {
268        Self::lower_into_rust_buffer(obj)
269    }
270}
271
272unsafe impl<UT, T: Lift<UT>> Lift<UT> for Option<T> {
273    type FfiType = RustBuffer;
274
275    fn try_read(buf: &mut &[u8]) -> Result<Option<T>> {
276        check_remaining(buf, 1)?;
277        Ok(match buf.get_i8() {
278            0 => None,
279            1 => Some(T::try_read(buf)?),
280            _ => bail!("unexpected tag byte for Option"),
281        })
282    }
283
284    fn try_lift(buf: RustBuffer) -> Result<Option<T>> {
285        Self::try_lift_from_rust_buffer(buf)
286    }
287}
288
289impl<UT, T: TypeId<UT>> TypeId<UT> for Option<T> {
290    const TYPE_ID_META: MetadataBuffer =
291        MetadataBuffer::from_code(metadata::codes::TYPE_OPTION).concat(T::TYPE_ID_META);
292}
293
294// Support for passing vectors of values via the FFI.
295//
296// Vectors are currently always passed by serializing to a buffer.
297// We write a `i32` item count followed by each item in turn.
298// (It's a signed type due to limits of the JVM).
299//
300// Ideally we would pass `Vec<u8>` directly as a `RustBuffer` rather
301// than serializing, and perhaps even pass other vector types using a
302// similar struct. But that's for future work.
303
304unsafe impl<UT, T: Lower<UT>> Lower<UT> for Vec<T> {
305    type FfiType = RustBuffer;
306
307    fn write(obj: Vec<T>, buf: &mut Vec<u8>) {
308        // TODO: would be nice not to panic here :-/
309        let len = i32::try_from(obj.len()).unwrap();
310        buf.put_i32(len); // We limit arrays to i32::MAX items
311        for item in obj {
312            <T as Lower<UT>>::write(item, buf);
313        }
314    }
315
316    fn lower(obj: Vec<T>) -> RustBuffer {
317        Self::lower_into_rust_buffer(obj)
318    }
319}
320
321/// Support for associative arrays via the FFI - `record<u32, u64>` in UDL.
322/// HashMaps are currently always passed by serializing to a buffer.
323/// We write a `i32` entries count followed by each entry (string
324/// key followed by the value) in turn.
325/// (It's a signed type due to limits of the JVM).
326unsafe impl<UT, T: Lift<UT>> Lift<UT> for Vec<T> {
327    type FfiType = RustBuffer;
328
329    fn try_read(buf: &mut &[u8]) -> Result<Vec<T>> {
330        check_remaining(buf, 4)?;
331        let len = usize::try_from(buf.get_i32())?;
332        let mut vec = Vec::with_capacity(len);
333        for _ in 0..len {
334            vec.push(<T as Lift<UT>>::try_read(buf)?)
335        }
336        Ok(vec)
337    }
338
339    fn try_lift(buf: RustBuffer) -> Result<Vec<T>> {
340        Self::try_lift_from_rust_buffer(buf)
341    }
342}
343
344impl<UT, T: TypeId<UT>> TypeId<UT> for Vec<T> {
345    const TYPE_ID_META: MetadataBuffer =
346        MetadataBuffer::from_code(metadata::codes::TYPE_VEC).concat(T::TYPE_ID_META);
347}
348
349unsafe impl<K, V, UT> Lower<UT> for HashMap<K, V>
350where
351    K: Lower<UT> + std::hash::Hash + Eq,
352    V: Lower<UT>,
353{
354    type FfiType = RustBuffer;
355
356    fn write(obj: HashMap<K, V>, buf: &mut Vec<u8>) {
357        // TODO: would be nice not to panic here :-/
358        let len = i32::try_from(obj.len()).unwrap();
359        buf.put_i32(len); // We limit HashMaps to i32::MAX entries
360        for (key, value) in obj {
361            <K as Lower<UT>>::write(key, buf);
362            <V as Lower<UT>>::write(value, buf);
363        }
364    }
365
366    fn lower(obj: HashMap<K, V>) -> RustBuffer {
367        Self::lower_into_rust_buffer(obj)
368    }
369}
370
371unsafe impl<K, V, UT> Lift<UT> for HashMap<K, V>
372where
373    K: Lift<UT> + std::hash::Hash + Eq,
374    V: Lift<UT>,
375{
376    type FfiType = RustBuffer;
377
378    fn try_read(buf: &mut &[u8]) -> Result<HashMap<K, V>> {
379        check_remaining(buf, 4)?;
380        let len = usize::try_from(buf.get_i32())?;
381        let mut map = HashMap::with_capacity(len);
382        for _ in 0..len {
383            let key = <K as Lift<UT>>::try_read(buf)?;
384            let value = <V as Lift<UT>>::try_read(buf)?;
385            map.insert(key, value);
386        }
387        Ok(map)
388    }
389
390    fn try_lift(buf: RustBuffer) -> Result<HashMap<K, V>> {
391        Self::try_lift_from_rust_buffer(buf)
392    }
393}
394
395impl<K, V, UT> TypeId<UT> for HashMap<K, V>
396where
397    K: TypeId<UT> + std::hash::Hash + Eq,
398    V: TypeId<UT>,
399{
400    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_HASH_MAP)
401        .concat(K::TYPE_ID_META)
402        .concat(V::TYPE_ID_META);
403}
404
405derive_ffi_traits!(blanket u8);
406derive_ffi_traits!(blanket i8);
407derive_ffi_traits!(blanket u16);
408derive_ffi_traits!(blanket i16);
409derive_ffi_traits!(blanket u32);
410derive_ffi_traits!(blanket i32);
411derive_ffi_traits!(blanket u64);
412derive_ffi_traits!(blanket i64);
413derive_ffi_traits!(blanket f32);
414derive_ffi_traits!(blanket f64);
415derive_ffi_traits!(blanket bool);
416derive_ffi_traits!(blanket String);
417derive_ffi_traits!(blanket Duration);
418derive_ffi_traits!(blanket SystemTime);
419
420// For composite types, derive LowerReturn, LiftReturn, etc, from Lift/Lower.
421//
422// Note that this means we don't get specialized return handling.  For example, if we could return
423// an `Option<Result<>>` we would always return that type directly and never throw.
424derive_ffi_traits!(impl<T, UT> LowerReturn<UT> for Option<T> where Option<T>: Lower<UT>);
425derive_ffi_traits!(impl<T, UT> LowerError<UT> for Option<T> where Option<T>: Lower<UT>);
426derive_ffi_traits!(impl<T, UT> LiftReturn<UT> for Option<T> where Option<T>: Lift<UT>);
427derive_ffi_traits!(impl<T, UT> LiftRef<UT> for Option<T> where Option<T>: Lift<UT>);
428
429derive_ffi_traits!(impl<T, UT> LowerReturn<UT> for Vec<T> where Vec<T>: Lower<UT>);
430derive_ffi_traits!(impl<T, UT> LowerError<UT> for Vec<T> where Vec<T>: Lower<UT>);
431derive_ffi_traits!(impl<T, UT> LiftReturn<UT> for Vec<T> where Vec<T>: Lift<UT>);
432derive_ffi_traits!(impl<T, UT> LiftRef<UT> for Vec<T> where Vec<T>: Lift<UT>);
433
434derive_ffi_traits!(impl<K, V, UT> LowerReturn<UT> for HashMap<K, V> where HashMap<K, V>: Lower<UT>);
435derive_ffi_traits!(impl<K, V, UT> LowerError<UT> for HashMap<K, V> where HashMap<K, V>: Lower<UT>);
436derive_ffi_traits!(impl<K, V, UT> LiftReturn<UT> for HashMap<K, V> where HashMap<K, V>: Lift<UT>);
437derive_ffi_traits!(impl<K, V, UT> LiftRef<UT> for HashMap<K, V> where HashMap<K, V>: Lift<UT>);
438
439// For Arc we derive all the traits, but have to write it all out because we need an unsized T bound
440derive_ffi_traits!(impl<T, UT> Lower<UT> for Arc<T> where Arc<T>: FfiConverter<UT>, T: ?Sized);
441derive_ffi_traits!(impl<T, UT> Lift<UT> for Arc<T> where Arc<T>: FfiConverter<UT>, T: ?Sized);
442derive_ffi_traits!(impl<T, UT> LowerReturn<UT> for Arc<T> where Arc<T>: Lower<UT>, T: ?Sized);
443derive_ffi_traits!(impl<T, UT> LowerError<UT> for Arc<T> where Arc<T>: Lower<UT>, T: ?Sized);
444derive_ffi_traits!(impl<T, UT> LiftReturn<UT> for Arc<T> where Arc<T>: Lift<UT>, T: ?Sized);
445derive_ffi_traits!(impl<T, UT> LiftRef<UT> for Arc<T> where Arc<T>: Lift<UT>, T: ?Sized);
446derive_ffi_traits!(impl<T, UT> TypeId<UT> for Arc<T> where Arc<T>: FfiConverter<UT>, T: ?Sized);
447
448// Implement LowerReturn/LiftReturn for the unit type (void returns)
449
450unsafe impl<UT> LowerReturn<UT> for () {
451    type ReturnType = ();
452
453    fn lower_return(_: ()) -> Result<Self::ReturnType, RustCallError> {
454        Ok(())
455    }
456}
457
458unsafe impl<UT> LiftReturn<UT> for () {
459    type ReturnType = ();
460
461    fn try_lift_successful_return(_: ()) -> Result<Self> {
462        Ok(())
463    }
464}
465
466impl<UT> TypeId<UT> for () {
467    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_UNIT);
468}
469
470// Implement LowerReturn/LiftReturn for `Result<R, E>`.  This is where we handle exceptions/Err
471// results.
472
473#[cfg(not(all(target_arch = "wasm32", feature = "wasm-unstable-single-threaded")))]
474unsafe impl<UT, R, E> LowerReturn<UT> for Result<R, E>
475where
476    R: LowerReturn<UT>,
477    E: LowerError<UT> + Display + Debug + Send + Sync + 'static,
478{
479    type ReturnType = R::ReturnType;
480
481    fn lower_return(v: Self) -> Result<Self::ReturnType, RustCallError> {
482        match v {
483            Ok(r) => R::lower_return(r),
484            Err(e) => Err(RustCallError::Error(E::lower_error(e))),
485        }
486    }
487
488    fn handle_failed_lift(error: crate::LiftArgsError) -> Result<Self::ReturnType, RustCallError> {
489        let crate::LiftArgsError { arg_name, error } = error;
490
491        let error = match error.downcast::<E>() {
492            Ok(user) => RustCallError::Error(<E as LowerError<UT>>::lower_error(user)),
493            Err(error) => crate::LiftArgsError { arg_name, error }.to_internal_error(),
494        };
495
496        Err(error)
497    }
498}
499
500#[cfg(all(target_arch = "wasm32", feature = "wasm-unstable-single-threaded"))]
501unsafe impl<UT, R, E> LowerReturn<UT> for Result<R, E>
502where
503    R: LowerReturn<UT>,
504    E: LowerError<UT> + Display + Debug + 'static,
505{
506    type ReturnType = R::ReturnType;
507
508    fn lower_return(v: Self) -> Result<Self::ReturnType, RustCallError> {
509        match v {
510            Ok(r) => R::lower_return(r),
511            Err(e) => Err(RustCallError::Error(E::lower_error(e))),
512        }
513    }
514}
515
516unsafe impl<UT, R, E> LiftReturn<UT> for Result<R, E>
517where
518    R: LiftReturn<UT>,
519    E: Lift<UT, FfiType = RustBuffer> + ConvertError<UT>,
520{
521    type ReturnType = R::ReturnType;
522
523    fn try_lift_successful_return(v: R::ReturnType) -> Result<Self> {
524        R::try_lift_successful_return(v).map(Ok)
525    }
526
527    fn lift_error(buf: RustBuffer) -> Self {
528        match E::try_lift_from_rust_buffer(buf) {
529            Ok(lifted_error) => Err(lifted_error),
530            Err(anyhow_error) => {
531                Self::handle_callback_unexpected_error(UnexpectedUniFFICallbackError {
532                    reason: format!("Error lifting from rust buffer: {anyhow_error}"),
533                })
534            }
535        }
536    }
537
538    fn handle_callback_unexpected_error(e: UnexpectedUniFFICallbackError) -> Self {
539        Err(E::try_convert_unexpected_callback_error(e).unwrap_or_else(|e| panic!("{e}")))
540    }
541}
542
543impl<UT, R, E> TypeId<UT> for Result<R, E>
544where
545    R: TypeId<UT>,
546    E: TypeId<UT>,
547{
548    const TYPE_ID_META: MetadataBuffer = MetadataBuffer::from_code(metadata::codes::TYPE_RESULT)
549        .concat(R::TYPE_ID_META)
550        .concat(E::TYPE_ID_META);
551}
552
553unsafe impl<T, UT> LiftRef<UT> for [T]
554where
555    T: Lift<UT>,
556{
557    type LiftType = Vec<T>;
558}
559
560unsafe impl<UT> LiftRef<UT> for str {
561    type LiftType = String;
562}