Rust Join the chat at https://gitter.im/abi<em>stable</em>crates/community api-docs

For Rust-to-Rust ffi, with a focus on creating libraries loaded at program startup, and with load-time type-checking.

This library allows defining Rust libraries that can be loaded at runtime. This isn't possible with the default (Rust) ABI and representation, since it's unstable.

These are some usecases for this library:

Features

Currently this library has these features:

Changelog

The changelog is in the "Changelog.md" file.

Example crates

For example crates using abi_stable you can look at the crates in the examples directory, in the repository for this crate.

To run the example crates you'll generally have to build the *_impl crate, then run the *_user crate (all *_user crates should have a help message).

These are the example crates:

Example

This is a full example, which is located in readme/readme_example, demonstrating:

User crate

This user crate (also called "application crate") depends on the interface crate with: toml [dependencies.readme_interface] path = "../readme_interface" its Rust code is: ```rust use abistable::stdtypes::RVec;

use readmeinterface::{ loadrootmoduleindirectory, AppenderBox, AppenderTO, BoxedInterface, ExampleLib_Ref, };

fn main() { // The type annotation is for the reader let library: ExampleLibRef = loadrootmoduleindirectory("../../../target/debug".asref()) .unwraporelse(|e| panic!("{}", e));

{
    /////////////////////////////////////////////////////////////////////////////////
    //
    //       This block demonstrates `#[sabi_trait]` generated trait objects
    //
    ////////////////////////////////////////////////////////////////////////////////

    // The type annotation is for the reader
    let mut appender: AppenderBox<u32> = library.new_appender()();
    appender.push(100);
    appender.push(200);

    // The primary way to use the methods in the trait is through the inherent methods on
    // the ffi-safe trait object.
    Appender_TO::push(&mut appender, 300);
    appender.append(vec![500, 600].into());
    assert_eq!(
        appender.into_rvec(),
        RVec::from(vec![100, 200, 300, 500, 600])
    );
}
{
    ///////////////////////////////////////////////////////////////////////////////////
    //
    //  This block demonstrates the `DynTrait<>` trait object.
    //
    //  `DynTrait` is used here as a safe opaque type which can only be unwrapped back to
    //  the original type in the dynamic library that constructed the `DynTrait` itself.
    //
    ////////////////////////////////////////////////////////////////////////////////////

    // The type annotation is for the reader
    let mut unwrapped: BoxedInterface = library.new_boxed_interface()();

    library.append_string()(&mut unwrapped, "Hello".into());
    library.append_string()(&mut unwrapped, ", world!".into());

    assert_eq!(&*unwrapped.to_string(), "Hello, world!");
}

println!("success");

}

``` note: the implementation crate must be compiled before this is ran, otherwise you'll get a runtime error, because the library couldn't be loaded.

Interface crate

```rust use std::path::Path;

use abistable::{ library::{LibraryError, RootModule}, packageversionstrings, sabitrait, sabitypes::VersionStrings, stdtypes::{RBox, RString, RVec}, DynTrait, StableAbi, };

/// This struct is the root module, /// which must be converted to ExampleLib_Ref to be passed through ffi. /// /// The #[sabi(kind(Prefix(prefix_ref = ExampleLib_Ref)))] /// attribute tells StableAbi to create an ffi-safe static reference type /// for ExampleLib called ExampleLib_Ref. /// /// The #[sabi(missing_field(panic))] attribute specifies that trying to /// access a field that doesn't exist must panic with a message saying that /// the field is inaccessible.

[repr(C)]

[derive(StableAbi)]

[sabi(kind(Prefix(prefixref = ExampleLibRef)))]

[sabi(missing_field(panic))]

pub struct ExampleLib { pub new_appender: extern "C" fn() -> AppenderBox,

pub new_boxed_interface: extern "C" fn() -> BoxedInterface<'static>,

/// The `#[sabi(last_prefix_field)]` attribute here means that this is the last
/// field in this struct that was defined in the first compatible version of the library
/// (0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
/// requiring new fields to always be added below preexisting ones.
///
/// The `#[sabi(last_prefix_field)]` attribute would stay on this field until the
/// library bumps its "major" version,
/// at which point it would be moved to the last field at the time.
///
#[sabi(last_prefix_field)]
pub append_string: extern "C" fn(&mut BoxedInterface<'_>, RString),

}

/// The RootModule trait defines how to load the root module of a library. impl RootModule for ExampleLibRef { abistable::declarerootmodulestatics! {ExampleLibRef}

const BASE_NAME: &'static str = "example_library";
const NAME: &'static str = "example_library";
const VERSION_STRINGS: VersionStrings = package_version_strings!();

}

/// This loads the root from the library in the directory folder. pub fn loadrootmoduleindirectory(directory: &Path) -> Result { ExampleLibRef::loadfrom_directory(directory) }

//////////////////////////////////////////////////////////

/// #[sabi_trait] is how one creates an ffi-safe trait object from a trait definition. /// /// In this case, the trait object is Appender_TO<'lt, Pointer<()>, Element>,where: /// /// - 'lt: /// Is the lifetime bound of the type that constructed the trait object /// ('static is the lifetime bound of objects that don't borrow anything). /// /// - Pointer<()>: /// Is any pointer that implements some abi_stable specific traits, /// this pointer owns the value that implements Appender. /// /// - Element: /// This is the element type of the collection that we operate on. /// This is a type parameter because it's a trait object, /// which turn associated types into type parameters. ///

[sabi_trait]

pub trait Appender { /// The element type of the collection. type Element;

/// Appends one element at the end of the collection.    
fn push(&mut self, value: Self::Element);

/// Appends many elements at the end of the collection.    
fn append(&mut self, vec: RVec<Self::Element>);

/// Converts this collection into an `RVec`.
///
/// As opposed to regular trait objects,
/// it is possible to call by-value methods on trait objects generated by `#[sabi_trait]`.
///
/// The `#[sabi(last_prefix_field)]` attribute here means that this is the last method
/// that was defined in the first compatible version of the library
/// (0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
/// requiring new methods to always be added below preexisting ones.
///
/// The `#[sabi(last_prefix_field)]` attribute would stay on this method until the library
/// bumps its "major" version,
/// at which point it would be moved to the last method at the time.
///
#[sabi(last_prefix_field)]
fn into_rvec(self) -> RVec<Self::Element>;

}

/// A type alias for the Appender trait object. /// /// 'static here means that the trait object cannot contain any borrows. pub type AppenderBox = Appender_TO<'static, RBox<()>, T>;

// Impls of local traits for dependencies have to be implemented in // the interface crate, because of the orphan rules. // // To avoid compiling more code than necessary, // this impl is not compiled by default. // it's enabled by the implementation crate but not the user crate.

[cfg(feature = "impls")]

impl Appender for RVec { type Element = T; fn push(&mut self, value: Self::Element) { self.push(value); } fn append(&mut self, vec: RVec) { self.extend(vec); } fn into_rvec(self) -> RVec { self } }

//////////////////////////////////////////////////////////

/// This type implements ÌnterfaceType /// (because of the #[sabi(impl_InterfaceType())] helper attribute of #[derive(StableAbi)] ), /// describing the traits required when constructing DynTrait<_, TheInterface>, /// and are then implemented by it.

[repr(C)]

[derive(StableAbi)]

[sabi(impl_InterfaceType(Sync, Send, Debug, Display))]

pub struct TheInterface;

/// An alias for the trait object used in this example pub type BoxedInterface<'borr> = DynTrait<'borr, RBox<()>, TheInterface>;

```

Implementation crate

This is the implementation crate, which is compiled as a cdylib (a dynamic library/shared object), and loaded by the user crate at runtime.

The important bits of its Cargo.toml file are: ```toml [lib] name = "readme_library" crate-type = ["cdylib",'rlib']

[dependencies.readmeinterface] path = "../readmeinterface" features = ["impls"] ``` its Rust code is:

```rust use std::fmt::{self, Display};

use readmeinterface::{AppenderBox, AppenderTO, BoxedInterface, ExampleLib, ExampleLib_Ref};

use abistable::{ exportrootmodule, prefixtype::PrefixTypeTrait, sabiexternfn, sabitrait::prelude::TDOpaque, std_types::{RString, RVec}, DynTrait, };

/// The function which exports the root module of the library. /// /// The root module is exported inside a static of LibHeader type, /// which has this extra metadata: /// /// - The abi_stable version number used by the dynamic library. /// /// - A constant describing the layout of the exported root module,and every type it references. /// /// - A lazily initialized reference to the root module. /// /// - The constructor function of the root module. ///

[exportrootmodule]

pub fn getlibrary() -> ExampleLibRef { ExampleLib { newappender, newboxedinterface, appendstring, } .leakintoprefix() }

/// DynTrait<_, TheInterface> is constructed from this type in this example

[derive(Debug, Clone)]

pub struct StringBuilder { pub text: String, pub appended: Vec, }

impl Display for StringBuilder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.text, f) } }

impl StringBuilder { /// Appends the string at the end. pub fn appendstring(&mut self, string: RString) { self.text.pushstr(&string); self.appended.push(string); } }

[sabiexternfn]

pub fn newappender() -> AppenderBox { // What TD_Opaque does here is specify that the trait object cannot be downcasted, // disallowing the Appender_TO from being unwrapped back into an RVec<u32> // when the trait_object.obj.*_downcast_*() methods are used. // // To be able to unwrap a #[sabi_trait] trait object back into the type it // was constructed with, you must: // // - Have a type that implements std::anu::Any // (it requires that the type doesn't borrow anything). // // - Pass TD_CanDowncast instead of TD_Opaque to // Appender_TO::{from_const, from_value,from_ptr}. // // - Unerase the trait object back into the original type with // trait_object.obj.downcast_into::<RVec<u32>>().unwrap() // (or the other downcasting methods). // // Downcasting a trait object will fail in any of these conditions: // // - It wasn't constructed in the same dynamic library. // // - It's not the same type. // // - It was constructed with TD_Opaque. // AppenderTO::fromvalue(RVec::new(), TDOpaque) }

/// Constructs a BoxedInterface.

[sabiexternfn]

fn newboxedinterface() -> BoxedInterface<'static> { DynTrait::from_value(StringBuilder { text: "".into(), appended: vec![], }) }

/// Appends a string to the erased StringBuilder.

[sabiexternfn]

fn appendstring(wrapped: &mut BoxedInterface<'>, string: RString) { wrapped .downcastasmut::() // Returns Result<&mut StringBuilder, _> .unwrap() // Returns &mut StringBuilder .append_string(string); }

```

Safety

This library ensures that the loaded libraries are safe to use through these mechanisms:

Note that this library assumes that dynamic libraries come from a benign source, these checks are done purely to detect programming errors.

Planned features

None right now.

Non-features (extremely unlikely to be added)

Supporting library unloading, since this requires building the entire library with the assumption that anything might get unloaded at any time.

Architecture

This is a way that users can structure their libraries to allow for dynamic linking.

For how to evolve dynamically loaded libraries loaded using the safe API in abi_stable look here.

Interface crate

A crate which declares:

Implementation crate

The crate compiled as a dynamic library that:

User crate

A crate that that declares the ìnterface crate as a dependency, and loads the pre-compiled implementation crate dynamic library from some path.

Minimum Rust version

This crate support Rust back to 1.61.0

You can manually enable support for Rust past 1.61.0 with the rust_*_* cargo features.

Crate Features

These are default cargo features that enable optional crates :

To disable the default features use: toml [dependencies.abi_stable] version = "<current_version>" default-features = false features = [ ] enabling the features you need in the features array.

Manually enabled

These are crate features to manually enable support for newer language features:

Glossary

interface crate: the crate that declares the public functions, types, and traits that are necessary to load a library at runtime.

ìmplementation crate: A crate that implements all the functions in a interface crate.

user crate: A crate that depends on an interface crate and loads 1 or more ìmplementation crates for it.

module: refers to a struct of function pointers and other static values. The root module of a library implements the [RootModule] trait. These are declared in the interface crate,exported in the implementation crate, and loaded in the user crate.

Tools

Here are some tools,all of which are in the "tools" directory(folder).

sabi_extract

A program to extract a variety of information from an abi_stable dynamic library.

License

abi_stable is licensed under either of

text Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in abi_stable by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.