Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Write safer FFI code in Rust without polluting it with unsafe code

License

NotificationsYou must be signed in to change notification settings

getditto/safer_ffi

Repository files navigation

safer-ffi-banner

CIguidedocs-rscrates-iorepository

What issafer_ffi?

safer_ffi is a framework that helps you write foreign function interfaces (FFI) without polluting your Rust code withunsafe { ... } code blocks while making functions far easier to read and maintain.

📚 Read The User Guide 📚

Prerequisites

Minimum Supported Rust Version:1.66.1

Quickstart

Click to hide

Small self-contained demo

You may try working with theexamples/point example embedded in the repo:

git clone https://github.com/getditto/safer_ffi&&cd safer_ffi(cd examples/point&& make)

Otherwise, to start using::safer_ffi, follow the following steps:

Crate layout

Step 1:Cargo.toml

Edit yourCargo.toml like so:

[package]name ="crate_name"version ="0.1.0"edition ="2021"[lib]crate-type = ["staticlib",# Ensure it gets compiled as a (static) C library# "cdylib",     # If you want a shared/dynamic C library (advanced)"lib",# For `generate-headers` and other downstream rust dependents# such as integration `tests/`, doctests, and `examples/`][[bin]]name ="generate-headers"required-features = ["headers"]# Do not build unless generating headers.[dependencies]# Use `cargo add` or `cargo search` to find the latest values of x.y.z.# For instance:#   cargo add safer-ffisafer-ffi.version ="x.y.z"safer-ffi.features = []# you may add some later on.[features]# If you want to generate the headers, use a feature-gate# to opt into doing so:headers = ["safer-ffi/headers"]
  • Where"x.y.z" ought to be replaced by the last released version, which youcan find by runningcargo search safer-ffi.

  • See thededicated chapter onCargo.toml for more info.

Step 2:src/lib.rs

Then, to export a Rust function to FFI, add the#[derive_ReprC] and#[ffi_export] attributeslike so:

use::safer_ffi::prelude::*;/// A `struct` usable from both Rust and C#[derive_ReprC]#[repr(C)]#[derive(Debug,Clone,Copy)]pubstructPoint{x:f64,y:f64,}/* Export a Rust function to the C world. *//// Returns the middle point of `[a, b]`.#[ffi_export]fnmid_point(a:&Point,b:&Point) ->Point{Point{x:(a.x + b.x) /2.,y:(a.y + b.y) /2.,}}/// Pretty-prints a point using Rust's formatting logic.#[ffi_export]fnprint_point(point:&Point){println!("{:?}", point);}// The following function is only necessary for the header generation.#[cfg(feature ="headers")]// c.f. the `Cargo.toml` sectionpubfngenerate_headers() ->::std::io::Result<()>{::safer_ffi::headers::builder().to_file("rust_points.h")?.generate()}

Step 3:src/bin/generate-headers.rs

fnmain() ->::std::io::Result<()>{::crate_name::generate_headers()}

Compilation & header generation

# Compile the C library (in `target/{debug,release}/libcrate_name.ext`)cargo build# --release# Generate the C headercargo run --features headers --bin generate-headers
Generated C header (rust_points.h)
/*! \file *//******************************************* *                                         * *  File auto-generated by `::safer_ffi`.  * *                                         * *  Do not manually edit this file.        * *                                         * *******************************************/#ifndef__RUST_CRATE_NAME__#define__RUST_CRATE_NAME__#ifdef__cplusplusextern"C" {#endif#include<stddef.h>#include<stdint.h>/** \brief *  A `struct` usable from both Rust and C */typedefstructPoint {/** <No documentation available> */doublex;/** <No documentation available> */doubley;}Point_t;/** \brief *  Returns the middle point of `[a, b]`. */Point_tmid_point (Point_tconst*a,Point_tconst*b);/** \brief *  Pretty-prints a point using Rust's formatting logic. */voidprint_point (Point_tconst*point);#ifdef__cplusplus}/* extern \"C\" */#endif#endif/* __RUST_CRATE_NAME__ */

Testing it from C

Here is a basic example to showcase FFI calling into our exported Rustfunctions:

main.c

#include<stdlib.h>#include"rust_points.h"intmain (intargc,charconst*constargv[]){Point_ta= { .x=84, .y=45 };Point_tb= { .x=0, .y=39 };Point_tm=mid_point(&a,&b);print_point(&m);returnEXIT_SUCCESS;}

Compilation command

cc -o main{,.c} -L target/debug -l crate_name -l{pthread,dl,m}# Now feel free to run the compiled binary./main
  • Note regarding the extra-l… flags.

    Those vary based on the version of the Rust standard library being used, andthe system being used to compile it. In order to reliably know which ones touse,rustc itself ought to be queried for it.

    Simple command:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null

    this yields,to the stderr, output along the lines of:

    note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.note: native-static-libs: -lSystem -lresolv -lc -lm -liconv

    Using something likesed -nE 's/^note: native-static-libs: (.*)/\1/p' isthus a convenient way to extract these flags:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null \2>&1| sed -nE's/^note: native-static-libs: (.*)/\1/p'

    Ideally, you would not query for this informationin a vacuum (e.g.,/dev/null file being used as input Rust code just above), and rather,would apply it for your actual code being compiled:

    cargo rustc -q -- --print=native-static-libs \2>&1| sed -nE's/^note: native-static-libs: (.*)/\1/p'

    And if you really wanted to polish things further, you could use theJSON-formatted compiler output (this, for instance, avoids having toredirectstderr). But then you'd have to use a JSON parser, such asjq:

    RUST_STDLIB_DEPS=$(set -eo pipefail&& \    cargo rustc \        --message-format=json \        -- --print=native-static-libs \| jq -r'        select (.reason == "compiler-message")        | .message.message'| sed -nE's/^native-static-libs: (.*)/\1/p' \)

    and then use:

    cc -o main{,.c} -L target/debug -l crate_name${RUST_STDLIB_DEPS}

which does output:

Point { x: 42.0, y: 42.0 }

🚀🚀

Development

Click to see

To test the code with a certain amount of FFI integration baked into the tests (sincesafer-ffi,alone, onlyexports APIs to the FFI, so doesn't come with FFI callsites on its own), theffi_tests/ project directory is used to test against C, C#, and Lua callsites.

You can run these tests directly by doing:

make -C ffi_tests

or by adding--features ffi-tests to thecargo test command.

Formatting

Code is formatted using the "{MSRV}-nightly toolchain". That is, formatting uses someunstablefeatures ofrustfmt, so we use a Versioned Nightly™ approach. For the sake of version consistency,we stick to that of our MSRV.

But since it needs to be a genuinenightly toolchain, we are forced to pick an actualnightlytoolchain, only one whose date matches the birth of the correspondingMSRV-stable toolchain.

To format the code, you have three options:

  • ./scripts/format.sh

  • cargo fmt-nightly (which is defined as an alias of the previous bullet)

  • cargo fmt but only after having run./scripts/formatting/setup_cargo_fmt_override.sh atleast once.

    This does mutate a bit yourrustup setup, but in an unobservable way (but for allowing thematching stable toolchain to useunstable features when runningcargo fmt, the very pointof this maneuver).

    This is probably the preferred approach for those with IDEs or whatnot which automagically runscargo fmt/rustfmt on save.

Dependencies

  • Lua dependencies

    For running the C# FFI integration tests please installdotnet (v8.0) dependency:

    Seehttps://aka.ms/dotnet-download for guidance about this.

  • Lua dependencies

    For running Lua FFI integration tests please installluajit dependency:

    MacOS:

    brew install luajit

    Ubuntu/Debian:

    sudo apt-get install -y luajit

Various integration test suites

safer-ffi includes three different tests suites that can be run.

# In the project root:cargotest# FFI testsmake -C ffi_tests# JavaScript testsmake -C js_tests# Running the JS tests also gives you instructions for running browser tests.# Run this command in the `js_tests` directory, open a browser and navigate to# http://localhost:13337/wasm-pack build --target web&& python3 -m http.server 13337

[8]ページ先頭

©2009-2025 Movatter.jp