[][src]Module abi_stable::docs::prefix_types

Prefix-types are types that derive StableAbi along with the #[sabi(kind(Prefix(prefix_struct="PrefixEquivalent")))] attribute. This is mostly intended for vtables and modules.

Prefix-types cannot directly be passed through ffi, instead they must be converted to the type declared with prefix_struct="PrefixEquivalent", and then pass &PrefixEquivalent instead.

To convert T to &PrefixEquivalent use either:

  • PrefixTypeTrait::leak_into_prefix:
    Which does the conversion directly,but leaks the value.

  • prefix_type::WithMetadata::new and then WithMetadata::as_prefix:
    Use this if you need a compiletime constant. First create a &'a WithMetadata<Self> constant, then use the WithMetadata::as_prefix method at runtime to cast it to &PrefixEquivalent.

All fields on &PrefixEquivalent are accessed through accessor methods with the same name as the fields.

To ensure that libraries stay abi compatible, the first minor version of the library must apply the #[sabi(last_prefix_field)] to some field and every minor version after that must add fields at the end (never moving that attribute). Changing the field that #[sabi(last_prefix_field)] is applied to is a breaking change.

Getter methods for fields after the one to which #[sabi(last_prefix_field)] was applied to will return Option<FieldType> by default,because those fields might not exist (the struct might come from a previous version of the library). To override how to deal with nonexistent fields, use the #[sabi(missing_field())] attribute, applied to either the struct or the field.

Grammar Reference

For the grammar reference,you can look at the documentation for #[derive(StableAbi)].

Examples

Example 1

Declaring a Prefix-type.


use abi_stable::{
    StableAbi,
    std_types::{RDuration,RStr},
};

#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_struct="Module")))]
#[sabi(missing_field(panic))]
pub struct ModuleVal {
    pub lib_name:RStr<'static>,

    #[sabi(last_prefix_field)]
    pub elapsed:extern fn()->RDuration,

    pub description:RStr<'static>,
}

Example 2:Declaring a type with a VTable

Here is the implementation of a Box-like type,which uses a VTable that is itself a Prefix.


use std::{
    ops::{Deref,DerefMut},
    marker::PhantomData,
    mem::ManuallyDrop,
};

use abi_stable::{
    StableAbi,
    extern_fn_panic_handling,
    pointer_trait::{CallReferentDrop, StableDeref, TransmuteElement},
    prefix_type::{PrefixTypeTrait,WithMetadata},
};

/// An ffi-safe `Box<T>`
#[repr(C)]
#[derive(StableAbi)]
pub struct BoxLike<T> {
    data: *mut T,
    
    // This can't be a `&'static BoxVtable<T>` because Rust will complain that 
    // `T` does not live for the `'static` lifetime.
    vtable: *const BoxVtable<T>,

    _marker: PhantomData<T>,
}


impl<T> BoxLike<T>{
    pub fn new(value:T)->Self{
        let box_=Box::new(value);
        
        Self{
            data:Box::into_raw(box_),
            vtable:unsafe{ (*BoxVtableVal::VTABLE).as_prefix() },
            _marker:PhantomData,
        }
    }

    // This is to get around a limitation of the type system where
    // vtables of generic types can't just be `&'static VTable<T>`
    // because it complains that T doesn't live forever.
    fn vtable<'a>(&self)->&'a BoxVtable<T>{
        unsafe{ &(*self.vtable) }
    }

    /// Extracts the value this owns.
    pub fn into_inner(self)->T{
        let this=ManuallyDrop::new(self);
        let vtable=this.vtable();
        unsafe{
            // Must copy this before calling `vtable.destructor()`
            // because otherwise it would be reading from a dangling pointer.
            let ret=this.data.read();
            vtable.destructor()(this.data,CallReferentDrop::No);
            ret
        }
    }
}


impl<T> Deref for BoxLike<T> {
    type Target=T;

    fn deref(&self)->&T{
        unsafe{
            &(*self.data)
        }
    }
}

impl<T> DerefMut for BoxLike<T> {
    fn deref_mut(&mut self)->&mut T{
        unsafe{
            &mut (*self.data)
        }
    }
}


impl<T> Drop for BoxLike<T>{
    fn drop(&mut self){
        let vtable=self.vtable();

        unsafe{
            vtable.destructor()(self.data,CallReferentDrop::Yes)
        }
    }
}


#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_struct="BoxVtable")))]
pub(crate) struct BoxVtableVal<T> {
    #[sabi(last_prefix_field)]
    destructor: unsafe extern "C" fn(*mut T, CallReferentDrop),
}


impl<T> BoxVtableVal<T>{
    const TMP0:Self=Self{
        destructor:destroy_box::<T>,
    };

    // This can't be a `&'static WithMetadata<Self>` because Rust will complain that 
    // `T` does not live for the `'static` lifetime.
    const VTABLE:*const WithMetadata<Self>={
        &WithMetadata::new(PrefixTypeTrait::METADATA,Self::TMP0)
    };
}

unsafe extern "C" fn destroy_box<T>(v: *mut T, call_drop: CallReferentDrop) {
    extern_fn_panic_handling! {
        let mut box_ = Box::from_raw(v as *mut ManuallyDrop<T>);
        if call_drop == CallReferentDrop::Yes {
            ManuallyDrop::drop(&mut *box_);
        }
        drop(box_);
    }
}

Example 3:module

This declares,initializes,and uses a module.

use abi_stable::{
    StableAbi,
    std_types::RDuration,
    prefix_type::PrefixTypeTrait,
};


#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_struct="PersonMod")))]
pub struct PersonModVal {

    // The getter for this field is infallible,defined (approximately) like this:
    // ```
    //  extern fn customer_for(&self)->extern fn(Id)->RDuration {
    //      self.customer_for
    //  }
    // ```
    #[sabi(last_prefix_field)]
    pub customer_for: extern fn(Id)->RDuration,

    // The default behavior for the getter is to return an Option<FieldType>,
    // if the field exists it returns Some(_),
    // otherwise it returns None.
    pub bike_count: extern fn(Id)->u32,

    // The getter for this field panics if the field doesn't exist.
    #[sabi(missing_field(panic))]
    pub visits: extern fn(Id)->u32,

    // The getter for this field returns `default_score()` if the field doesn't exist.
    #[sabi(missing_field(with="default_score"))]
    pub score: extern fn(Id)->u32,
    
    // The getter for this field returns `Default::default()` if the field doesn't exist.
    #[sabi(missing_field(default))]
    pub visit_length: Option< extern fn(Id)->RDuration >,

}

fn default_score()-> extern fn(Id)->u32 {
    extern fn default(_:Id)->u32{
        1000
    }

    default
}

type Id=u32;


/*
    ...
    Elided function definitions
    ...
*/


let module:&'static PersonMod=
    PersonModVal{
        customer_for,
        bike_count,
        visits,
        score,
        visit_length:None,
    }.leak_into_prefix();


// Getting the value for every field of `module`.

let customer_for: extern fn(Id)->RDuration = 
    module.customer_for();

let bike_count: Option<extern fn(Id)->u32> = 
    module.bike_count();

let visits: extern fn(Id)->u32=
    module.visits();

let score: extern fn(Id)->u32=
    module.score();

let visit_length: Option<extern fn(Id)->RDuration> =
    module.visit_length();