#haskell #bindings #generate #macro #handy #c-ffi #features

无std hs-bindgen

为Haskell生成Rust的C-FFI绑定的便捷宏

15个不稳定版本

0.9.0 2023年12月6日
0.8.0 2023年1月27日
0.7.1 2022年12月5日
0.7.0 2022年11月15日

#555 in Rust模式

每月32次下载

MIT/Apache

13KB

hs-bindgen

为Haskell生成Rust的C-FFI绑定的便捷宏。

此库旨在与通过 cargo-cabal 配置的项目最佳配合。

注意。 MSRV是 1.64.0,因为它使用 core_ffi_c 功能。

示例

一个最小示例是将函数注释如下

use hs_bindgen::*;

/// Haskell type signatures are auto-magically inferred from Rust function
/// types! This feature could slow down compilation, and be enabled with:
/// `hs-bindgen = { ..., features = [ "full" ] }`
#[hs_bindgen]
fn greetings(name: &str) {
    println!("Hello, {name}!");
}

这将被扩展为(您可以用 cargo expand 尝试自己)

use hs_bindgen::*;

fn greetings(name: &str) {
    println!("Hello, {name}!");
}

#[no_mangle] // Mangling makes symbol names more difficult to predict.
             // We disable it to ensure that the resulting symbol is really `__c_greetings`.
extern "C" fn __c_greetings(__0: *const core::ffi::c_char) -> () {
    // `traits` module is `hs-bindgen::hs-bindgen-traits`
    // n.b. do not forget to import it, e.g., with `use hs-bindgen::*`
    traits::FromReprC::from(greetings(traits::FromReprRust::from(__0),))
}

一个更完整的示例,当我们现在尝试将自定义类型传递到我们的接口时

use hs_bindgen::{traits::FromReprRust, *};
use std::marker::PhantomData;

/// A custom Rust data-type, `#[repr(transparent)]` is not useful here
/// since `FromReprRust` trait will offers the constructor we need to construct
/// our type out of a C-FFI safe primitive data-structure.
struct User<T: Kind> {
    name: String,
    kind: PhantomData<T>,
}

/** Overly engineered traits definitions just for the sake of demonstrating
limitations of this example, this isn't at all needed by default */

struct Super;

trait Kind {
    fn greet(name: &str) -> String;
}

impl Kind for Super {
    fn greet(name: &str) -> String {
        format!("Hello, {}!", name)
    }
}

/// Declare targeted Haskell signature, return types should be wrapped in
/// an IO Monad (a behavior enforced by safety concerns)
#[hs_bindgen(hello :: CString -> IO CString)]
fn hello(user: User<Super>) -> String {
    Super::greet(&user.name)
}

/** n.b. functions wrapped by `#[hs_bindgen]` macro couldn't be
parametrized by generics (because monomorphisation occurs after macro
expansion during compilation, and how rustc assign unmangled symbols to
monomorphised methods are AFAIK not a publicly specified behavior), but
this limitation didn’t apply to `hs-bindgen-traits` implementations! */

impl<T: Kind> FromReprRust<*const i8> for User<T> {
    fn from(ptr: *const i8) -> Self {
        User::<T> {
            name: <String as FromReprRust<*const i8>>::from(ptr),
            kind: PhantomData::<T>
        }
    }
}

设计

首先,我要感谢 Michael Gattozzi,他实现了 一个不再维护的绑定生成实现,并且 他的写作 和指导真正帮助我快速启动这个项目。

我尝试用以下核心设计原则来构建 hs-bindgen

  • 简单性: 遵循KISS UNIX哲学的简约主义,意味着在这里我试图从不重新实现Rust编程语言已经处理的功能(解析代码、推断类型等),而是依赖于宏和特质的系统功能。例如,这个代码中唯一剩下的解析部分是Haskell函数签名(鉴于授权的C-FFI安全类型的特性集,这是微不足道的);

  • 模块化: 此库旨在适用于更广泛的用途,因此此库应该可以在 #[no_std] 设置下工作,并且大多数功能可以禁用。例如,由 antlion 库提供的类型推断是可选的;

  • 稳定性:该库在稳定C ABI(具有良好定义的内存布局约定)范围内不实现任何技巧,并确保在不违反此安全规则的前提下提供易用性。任何rustc或GHC更新都无法打破任何魔法!

致谢

⚠️ 这仍然是一个工作实验,尚未准备好投入生产。

hs-bindgen受到了其他互操作性项目的很大启发,如wasm-bindgenPyO3

该项目是作为IOG承包商的工作任务的一部分。

许可证

根据您的选择,该许可证受Apache许可证版本2.0或MIT许可证的约束。

除非您明确声明,否则根据Apache-2.0许可证定义的,您有意提交给本项目的内容,将按上述方式双重许可,没有任何额外的条款或条件。

依赖关系

~0.6–1.2MB
~27K SLoC