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 |
#55 in FFI
5,051,027 downloads per month
Used in 10,095 crates
(67 directly)
17KB
96 lines
foreign-types
A framework for Rust wrappers over C APIs.
License
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 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
~225–670KB
~16K SLoC