Giter VIP home page Giter VIP logo

foreign-types's Introduction

foreign-types

Build Status

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.

foreign-types's People

Contributors

avitex avatar daxpedda avatar flier avatar ignatenkobrain avatar jyn514 avatar kornelski avatar sfackler avatar shepmaster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

foreign-types's Issues

Provide example of providing a lifetime parameter

I tried to use the generics support in foreign_types like this:

foreign_type! {
    pub unsafe type Feature<'a> {
        type CType = libc::c_void;
        type PhantomData = 'a;
        fn drop = feature_dtor;
    }
}

The compiler error I get is:

error: at least one trait is required for an object type
  --> src/foo/bar.rs:21:28
   |
21 |         type PhantomData = 'a;
   |                            ^^

Is there some other way of doing this using the macro? Or maybe I'm using it incorrectly?

Consider changing back to `macro_rules!`?

In c99fda3 the foreign_type! macro was switched from a macro_rules! macro/"macro by example" to a procedural macro. This allows more advanced things like creating new identifiers (user specifies Foo, macro creates FooRef).

However, there are a few downsides to using a procedural macro, in my eyes the most important one being compile times (but I personally also find proc macro code harder to read, and tools like rust-analyzer have a harder time with it) - as an example, @avitex' update of foreign-types in core-foundation-rs makes the build go from ~20s to ~30s on my machine, because of the added proc macro dependencies and compilation.

Would you consider going back to using macro_rules!? I would probably be willing to implement this, the biggest hurdle is probably that the syntax would have to be changed again so that the FooRef identifier is specified by the user. A few ideas:

// Current
foreign_type! {
    /// Foo doc
    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
    }
}

// Idea #1
foreign_type! {
    /// Foo doc
    pub unsafe type Foo: Sync + Send // optional
    {
        type CType = foo_sys::FOO;
        /// FooRef doc
        pub type RefType = FooRef;
        fn drop = foo_sys::FOO_free;
        fn clone = foo_sys::FOO_duplicate; // optional
    }
}

// Idea #2
foreign_type! {
    /// Foo doc
    pub unsafe type Foo: Sync + Send // optional
    {
        /// FooRef doc
        pub type FooRef = foo_sys::FOO;
        fn drop = foo_sys::FOO_free;
        fn clone = foo_sys::FOO_duplicate; // optional
    }
}

// Idea #3
foreign_type! {
    /// Foo doc
    pub unsafe type Foo: Sync + Send // optional
    {
        /// FooRef doc
        pub ref type FooRef; // Possibly also with Sync and Send here?
        type CType = foo_sys::FOO;
        fn drop = foo_sys::FOO_free;
        fn clone = foo_sys::FOO_duplicate; // optional
    }
}

as_ptr()'s use of *mut is misleading

as_ptr() returns a *mut _ pointer. This isn't an error by itself, but under Rust's memory model, this pointer is derived from shared/immutable &self, and therefore doesn't have permission to mutate self, so the mut of the pointer is misleading.

fn as_ptr(&self) -> *mut Self::CType {

I assume this is for convenience, because C APIs aren't diligent about const vs mut distinction. However, this is a gotcha, because C APIs that take *mut may actually mutate the object, and that is UB from Rust's perspective.

Could you add .as_mut_ptr() that takes &mut self to provide a pointer safe for mutation?

We don't actually need a macro

My Rust Analyzer did not handle well the structs being generated as the outcome of a macro, so I decided to not use macros at all.
In my code, I've implemented a helper ctype module which uses a CTypeMeta trait in order to know how to implement the actual ForeignType and ForeignTypeRef.

This is my full code:

use std::{
    borrow::{Borrow, BorrowMut},
    ptr::NonNull,
};

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

pub trait CTypeMeta {
    type CType;

    unsafe fn drop_ptr(ptr: *mut Self::CType);
    unsafe fn clone_ptr(ptr: *mut Self::CType) -> *mut Self::CType;
}

pub struct CTypeOwned<T: CTypeMeta>(NonNull<T::CType>);
pub struct CTypeRef<T: CTypeMeta>(Opaque, std::marker::PhantomData<T>);

unsafe impl<T: CTypeMeta> ForeignType for CTypeOwned<T> {
    type CType = T::CType;
    type Ref = CTypeRef<T>;

    unsafe fn from_ptr(ptr: *mut Self::CType) -> Self {
        Self(NonNull::new_unchecked(ptr))
    }

    fn as_ptr(&self) -> *mut Self::CType {
        self.0.as_ptr()
    }
}
unsafe impl<T: CTypeMeta> ForeignTypeRef for CTypeRef<T> {
    type CType = T::CType;
}
unsafe impl<T: CTypeMeta> Sync for CTypeRef<T> {}
unsafe impl<T: CTypeMeta> Send for CTypeRef<T> {}
unsafe impl<T: CTypeMeta> Sync for CTypeOwned<T> {}
unsafe impl<T: CTypeMeta> Send for CTypeOwned<T> {}

impl<T: CTypeMeta> Deref for CTypeOwned<T> {
    type Target = <Self as ForeignType>::Ref;

    fn deref(&self) -> &Self::Target {
        unsafe { <Self as ForeignType>::Ref::from_ptr(self.as_ptr()) }
    }
}

impl<T: CTypeMeta> DerefMut for CTypeOwned<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { <Self as ForeignType>::Ref::from_ptr_mut(self.as_ptr()) }
    }
}

impl<T: CTypeMeta> Drop for CTypeOwned<T> {
    fn drop(&mut self) {
        unsafe { T::drop_ptr(self.as_ptr()) }
    }
}

impl<T: CTypeMeta> ToOwned for CTypeRef<T> {
    type Owned = CTypeOwned<T>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { Self::Owned::from_ptr(T::clone_ptr(self.as_ptr())) }
    }
}

impl<T: CTypeMeta> Borrow<CTypeRef<T>> for CTypeOwned<T> {
    fn borrow(&self) -> &CTypeRef<T> {
        unsafe { CTypeRef::<T>::from_ptr(self.as_ptr()) }
    }
}
impl<T: CTypeMeta> BorrowMut<CTypeRef<T>> for CTypeOwned<T> {
    fn borrow_mut(&mut self) -> &mut CTypeRef<T> {
        unsafe { CTypeRef::<T>::from_ptr_mut(self.as_ptr()) }
    }
}

Usage example of this code would be:

pub struct MyTypeMeta();
impl CTypeMeta for MyTypeMeta {
    CType = mtype_t;

    unsafe fn drop_ptr(ptr: *mut Self::CType) {
        mtype_free(ptr);
    }
    unsafe fn clone_ptr(ptr: *mut Self::CType) -> *mut Self::CType {
        mtype_clone(ptr);
    }
}
pub type MyType = CTypeOwned<MyTypeMeta>;
pub type MyTypeRef = CTypeRef<MyTypeMeta>;
impl MyType {
    // etc
}
impl MyTypeRef {
    // etc
}

We may need a `Send`/`Sync` wrapper instead of `NonNull<T>`

As you known, when we declare a foreign type that implements Send and Sync

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
    }
}

It will generate a type that wrap the NonNull<T>.

pub struct FooRef(Opaque);

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

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 {}

Then the nightly clippy will give a non_send_fields_in_send_ty warning, because NonNull<T> is !Send and !Sync.

   |
13 | / foreign_type! {
14 | |     /// A compiled pattern database that can then be used to scan data.
15 | |     pub unsafe type Database<T>: Send + Sync {
16 | |         type CType = ffi::hs_database_t;
...  |
20 | |     }
21 | | }
   | |_^
   |
   = note: `#[warn(clippy::non_send_fields_in_send_ty)]` on by default
note: the type of field `0` is `!Send`

We need a Send/Sync wrapper instead of NonNull<T>, maybe change to use UnsafeCell<NonNull<T>>?

Hiding unsafety.

So, I was looking into a crev review of this crate: MaulingMonkey/crev-proofs@3a450f6 and I kind of agree with the conclusion there. It seems that using foreign_type! allows skipping the unsafe keyword and potentially obscuring to the reviewer that the underlying code contains unsafe.

Since this is a macro and not a function, it remains a bit of a gray area, I guess. But I just wanted to bring it up.

Maybe the macro should always be invoked like:

unsafe {
  foreign_type! {
     ...
  }
}

or alternatively, maybe it should be at least named unsafe_foreign_type!.

Use of uninhabited enums

This library suggests using uninhabited enums to represent opaque types. From the first example:

mod foo_sys {
    pub enum FOO {}

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

I'm not entirely sure, but I think using uninhabited enums are here is undefined behaviour (though it currently emits the same LLVM IR), see this entry in The Rustonomicon. The recommended approach should be zero sized #[repr(C)] structs (or extern types when they stabilize).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.