#wrapper #api #framework #type #foreign

no-std foreign-types

A framework for Rust wrappers over C APIs

8 releases (4 breaking)

0.5.0 Oct 13, 2019
0.4.0 Mar 4, 2019
0.3.2 Nov 28, 2017
0.3.0 Sep 5, 2017
0.1.0 Jan 31, 2017

#1208 in Rust patterns

Download history 682939/week @ 2023-11-21 812124/week @ 2023-11-28 825677/week @ 2023-12-05 778550/week @ 2023-12-12 642029/week @ 2023-12-19 412172/week @ 2023-12-26 720591/week @ 2024-01-02 766562/week @ 2024-01-09 842883/week @ 2024-01-16 848574/week @ 2024-01-23 895498/week @ 2024-01-30 900970/week @ 2024-02-06 856196/week @ 2024-02-13 909298/week @ 2024-02-20 933086/week @ 2024-02-27 778239/week @ 2024-03-05

3,627,126 downloads per month
Used in 8,228 crates (54 directly)

MIT/Apache

17KB
96 lines

foreign-types

CircleCI

Documentation

A framework for Rust wrappers over C APIs.

License

Licensed under either of

at your option.

Contribution

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


lib.rs:

A framework for Rust wrappers over C APIs.

Ownership is as important in C as it is in Rust, but the semantics are often implicit. In particular, pointer-to-value is commonly used to pass C values both when transferring ownership or a borrow.

This crate provides a framework to define a Rust wrapper over these kinds of raw C APIs in a way that allows ownership semantics to be expressed in an ergonomic manner. The framework takes a dual-type approach similar to APIs in the standard library such as PathBuf/Path or String/ str. One type represents an owned value and references to the other represent borrowed values.

Examples

use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;

mod foo_sys {
    pub enum FOO {}

    extern {
        pub fn FOO_free(foo: *mut FOO);
    }
}

// The borrowed type is a newtype wrapper around an `Opaque` value.
//
// `FooRef` values never exist; we instead create references to `FooRef`s
// from raw C pointers.
pub struct FooRef(Opaque);

unsafe impl ForeignTypeRef for FooRef {
    type CType = foo_sys::FOO;
}

// The owned type is simply a newtype wrapper around the raw C type.
//
// It dereferences to `FooRef`, so methods that do not require ownership
// should be defined there.
pub struct Foo(NonNull<foo_sys::FOO>);

unsafe impl Sync for FooRef {}
unsafe impl Send for FooRef {}

unsafe impl Sync for Foo {}
unsafe impl Send for Foo {}

impl Drop for Foo {
    fn drop(&mut self) {
        unsafe { foo_sys::FOO_free(self.as_ptr()) }
    }
}

unsafe impl ForeignType for Foo {
    type CType = foo_sys::FOO;
    type Ref = FooRef;

    unsafe fn from_ptr(ptr: *mut foo_sys::FOO) -> Foo {
        Foo(NonNull::new_unchecked(ptr))
    }

    fn as_ptr(&self) -> *mut foo_sys::FOO {
        self.0.as_ptr()
    }

    fn into_ptr(self) -> *mut foo_sys::FOO {
        let inner = self.as_ptr();
        ::core::mem::forget(self);
        inner
    }
}

impl Deref for Foo {
    type Target = FooRef;

    fn deref(&self) -> &FooRef {
        unsafe { FooRef::from_ptr(self.as_ptr()) }
    }
}

impl DerefMut for Foo {
    fn deref_mut(&mut self) -> &mut FooRef {
        unsafe { FooRef::from_ptr_mut(self.as_ptr()) }
    }
}

// add in Borrow, BorrowMut, AsRef, AsRefMut, Clone, ToOwned...

The foreign_type! macro can generate this boilerplate for you:

use foreign_types::foreign_type;

mod foo_sys {
    pub enum FOO {}

    extern {
        pub fn FOO_free(foo: *mut FOO);
        pub fn FOO_duplicate(foo: *mut FOO) -> *mut FOO; // optional
    }
}

foreign_type! {
    /// A Foo.
    pub unsafe type Foo
        : Sync + Send // optional
    {
        type CType = foo_sys::FOO;
        fn drop = foo_sys::FOO_free;
        fn clone = foo_sys::FOO_duplicate; // optional
    }

    /// A Foo with generic parameters.
    pub unsafe type GenericFoo<T> {
        type CType = foo_sys::FOO;
        // This type is added as a `PhantomData` field to handle variance
        // of the parameters. However, it has no impact on trait impls:
        // `GenericFoo<T>` is always `Clone`, even if `T` is not.
        type PhantomData = T;
        fn drop = foo_sys::FOO_free;
        fn clone = foo_sys::FOO_duplicate;
    }
}

If fn clone is specified, then it must take CType as an argument and return a copy of it as CType. It will be used to implement Clone, and if the std Cargo feature is enabled, ToOwned.

Say we then have a separate type in our C API that contains a FOO:

mod foo_sys {
    pub enum FOO {}
    pub enum BAR {}

    extern {
        pub fn FOO_free(foo: *mut FOO);
        pub fn BAR_free(bar: *mut BAR);
        pub fn BAR_get_foo(bar: *mut BAR) -> *mut FOO;
    }
}

The documentation for the C library states that BAR_get_foo returns a reference into the BAR passed to it, which translates into a reference in Rust. It also says that we're allowed to modify the FOO, so we'll define a pair of accessor methods, one immutable and one mutable:

use foreign_types::{ForeignTypeRef, foreign_type};

mod foo_sys {
    pub enum FOO {}
    pub enum BAR {}

    extern {
        pub fn FOO_free(foo: *mut FOO);
        pub fn BAR_free(bar: *mut BAR);
        pub fn BAR_get_foo(bar: *mut BAR) -> *mut FOO;
    }
}

foreign_type! {
    /// A Foo.
    pub unsafe type Foo: Sync + Send {
        type CType = foo_sys::FOO;
        fn drop = foo_sys::FOO_free;
    }

    /// A Bar.
    pub unsafe type Bar: Sync + Send {
        type CType = foo_sys::BAR;
        fn drop = foo_sys::BAR_free;
    }
}

impl BarRef {
    fn foo(&self) -> &FooRef {
        unsafe { FooRef::from_ptr(foo_sys::BAR_get_foo(self.as_ptr())) }
    }

    fn foo_mut(&mut self) -> &mut FooRef {
        unsafe { FooRef::from_ptr_mut(foo_sys::BAR_get_foo(self.as_ptr())) }
    }
}

Dependencies

~0.4–0.8MB
~20K SLoC