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:
-
Converting a Rust dependency tree from compiling statically into a single binary, into one binary (and potentially) many dynamic libraries, allowing separate re-compilation on changes.
-
Creating a plugin system (without support for unloading).
Features
Currently this library has these features:
-
Features the
sabi_trait
attribute macro, for creating ffi-safe trait objects. -
Ffi-safe equivalent of some trait objects with
DynTrait
. -
Provides ffi-safe alternatives/wrappers for many standard library types, in the
std_types
module. -
Provides ffi-safe wrappers for some types defined in external crates, in the
external_types
module. -
Provides the
StableAbi
trait for asserting that types are ffi-safe. -
The prefix types feature for building extensible modules and vtables, without breaking ABI compatibility.
-
Supports ffi-safe nonexhaustive enums, wrapped in
NonExhaustive
. -
Checking at load-time that the types in the dynamic library have the expected layout, allowing for semver compatible changes while checking the layout of types.
-
Provides the
StableAbi
derive macro to both assert that the type is ffi compatible, and to get the layout of the type at load-time to check that it is still compatible.
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:
-
0 - modules and interface types: Demonstrates abi_stable "modules"(structs of function pointers), and interface types through a command line application with a dynamically linked backend.
-
1 - trait objects: Demonstrates ffi-safe trait objects (Generated using the
sabi_trait
attribute macro) by creating a minimal plugin system. -
2 - nonexhaustive-enums: Demonstrates nonexhaustive-enums as parameters and return values, for an application that manages the catalogue of a shop.
Example
This is a full example, which is located in examples/readme_example
, demonstrating:
-
user crates
(defined in the Architecture section below). -
Ffi-safe trait objects, generated through the
sabi_trait
attribute macro. -
DynTrait
: An ffi-safe multi-trait object for a selection of traits, which can also be downcast back into the concrete type. -
interface crates
(defined in the Architecture section below). -
ìmplementation crates
(defined in the Architecture section below).
User crate
This user crate (also called "application crate") depends on the interface crate with:
[]
= "../readme_interface"
its Rust code is:
use RVec;
use ;
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
use Path;
use ;
/// 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.
/// The RootModule trait defines how to load the root module of a library.
/// This loads the root from the library in the `directory` folder.
//////////////////////////////////////////////////////////
/// `#[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.
///
/// A type alias for the Appender trait object.
///
/// `'static` here means that the trait object cannot contain any borrows.
pub type AppenderBox<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.
//////////////////////////////////////////////////////////
/// 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.
;
/// An alias for the trait object used in this example
pub type BoxedInterface<'borr> = ;
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:
[]
= "readme_library"
= ["cdylib",'rlib']
[]
= "../readme_interface"
= ["impls"]
its Rust code is:
use ;
use ;
use ;
/// 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.
///
/// `DynTrait<_, TheInterface>` is constructed from this type in this example
/// Constructs a BoxedInterface.
/// Appends a string to the erased `StringBuilder`.
Safety
This library ensures that the loaded libraries are safe to use through these mechanisms:
-
The abi_stable ABI of the library is checked, Each
0.y.0
version andx.0.0
version of abi_stable defines its own ABI which is incompatible with previous versions. -
Types are recursively checked when the dynamic library is loaded, before any function can be called.
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:
-
The root module (a struct of function pointers/other modules), which implements the
RootModule
trait, exported from the dynamic library. -
All the sub-modules of the root module.
-
All the public types passed to and returned by the functions.
-
Optionally: declare the ffi-safe traits with the
sabi_trait
attribute, used as trait objects in the public interface. -
Optionally: declares ìnterface types,types which implement
InterfaceType
, used to specify the traits usable in theDynTrait
ffi-safe trait object .
Implementation crate
The crate compiled as a dynamic library that:
-
Implements all the functions declared in the
interface crate
. -
Declares a function to export the root module, using the
export_root_module
attribute to export the module. -
Optionally: Implement traits that were annotated with the
sabi_trait
attribute, constructing their trait objects exposed in the public API.
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 :
-
"channels": Depends on
crossbeam-channel
, wrapping channels from it for ffi inabi_stable::external_types::crossbeam_channel
. -
"serde_json": Depends on
serde_json
, providing ffi-safe equivalents of&serde_json::value::RawValue
andBox<serde_json::value::RawValue>
, inabi_stable::external_types::serde_json
.
To disable the default features use:
[]
= "<current_version>"
= false
= [ ]
enabling the features you need in the features
array.
Manually enabled
These are crate features to manually enable support for newer language features:
-
"rust_1_64": Turns many functions for converting types to slices into const fns.
-
"rust_latest_stable": Enables the "rust_1_*" features for all the stable releases.
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 crate
s 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
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.