Show HN: Hsrs – 用于 Rust 的类型安全 Haskell 绑定生成器
摘要
Hsrs 是一个类型安全的 FFI 绑定生成器,允许从 Haskell 调用 Rust 代码,具有自动内存管理、类型转换和 Borsh 序列化功能。它在 Rust 中提供注解,并生成符合语言习惯的 Haskell 包装器。
查看缓存全文
缓存时间: 2026/05/19 07:01
harmont-dev/hsrs
来源:https://github.com/harmont-dev/hsrs
hsrs 操作状态
actions status
codecov
crates.io
最低 rustc 版本 1.85 (https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html)
discord
从 Haskell 调用 Rust,使用类型安全、自动生成的 FFI 绑定。
给你的 Rust 类型和函数加上注解,运行代码生成器,就能得到符合 Haskell 惯用法的代码,它会自动处理内存管理、序列化和类型转换。
快速开始
1. 注解你的 Rust 代码
#[hsrs::module]
mod canvas {
#[hsrs::value_type]
pub struct Point {
pub x: i32,
pub y: i32,
}
#[hsrs::data_type]
pub struct Canvas {
points: Vec<Point>,
}
impl Canvas {
#[hsrs::function]
pub fn new() -> Self {
Self { points: vec![] }
}
#[hsrs::function]
pub fn add_point(&mut self, p: Point) {
self.points.push(p);
}
#[hsrs::function]
pub fn count(&self) -> u64 {
self.points.len() as u64
}
}
}
2. 生成 Haskell 绑定
cargo install hsrs-codegen
hsrs-codegen src/lib.rs -o Bindings.hs
3. 在 Haskell 中使用
import Bindings
main :: IO ()
main = do
c <- new
addPoint c (Point 10 20)
n <- count c
print n -- 输出 1
就是这样。内存通过 ForeignPtr 自动管理,复杂的类型(如 Point)则通过 Borsh (https://borsh.io) 在边界上进行序列化。
环境搭建
Rust 侧 —— 在 crate 中添加 hsrs:
[lib]
crate-type = ["lib", "staticlib"]
[dependencies]
hsrs = "0.1"
Haskell 侧 —— 添加 hsrs 运行时包:
build-depends: hsrs >= 0.1 && < 0.2
这会自动引入 Borsh 序列化,无需额外依赖。
可注解的内容
| 注解 | 作用 | Haskell 结果 |
|---|---|---|
#[hsrs::data_type] | 通过指针传递的不透明结构体 | ForeignPtr 新类型,自动清理 |
#[hsrs::enumeration] | 与 C 兼容的枚举(repr(u8)) | Word8 新类型,带有模式同义词 |
#[hsrs::value_type] | 通过 Borsh 按值传递的结构体 | data 记录,派生 Borsh |
#[hsrs::function] | 通过 FFI 导出的方法 | 类型安全的 Haskell 包装器 |
#[hsrs::module] | 将数据类型及其方法分组 | 为该类型生成所有 FFI 胶水代码 |
Result 变为 Either E T,Option 变为 Maybe T,Vec<T> 变为 [T],String 变为 Text —— 所有类型都通过 Borsh 透明地序列化。
支持的类型
| Rust | Haskell | 传输方式 |
|---|---|---|
i8, i16, i32, i64 | Int8, Int16, Int32, Int64 | 直接 (C FFI) |
u8, u16, u32, u64 | Word8, Word16, Word32, Word64 | 直接 (C FFI) |
bool | CBool | 直接 (C FFI) |
usize / isize | Word64 / Int64 | 直接 (C FFI) |
#[hsrs::enumeration] 枚举 | Word8 新类型 + 模式 | 直接 (C FFI) |
#[hsrs::value_type] 结构体 | data 记录 | Borsh |
String | Text | Borsh |
Vec<T> | [T] | Borsh |
Option<T> | Maybe T | Borsh |
Result<T, E> | Either E T | Borsh |
平台说明
usize 和 isize 分别映射为 Word64 和 Int64。这与 64 位平台(x86_64、aarch64)一致。如果你面向 32 位平台,请注意值可能被截断。
完整示例
一个带有枚举、值类型、Result 和 Option 的小型虚拟机
Rust
#[hsrs::module]
mod quecto_vm {
#[derive(Debug, PartialEq, Eq)]
#[hsrs::enumeration]
pub enum Register {
Reg0,
Reg1,
Count,
}
#[derive(Debug, PartialEq, Eq)]
#[hsrs::value_type]
pub struct Point {
pub x: i32,
pub y: i32,
}
#[derive(Debug, PartialEq, Eq)]
#[hsrs::value_type]
pub struct VmError {
pub code: u32,
}
#[hsrs::data_type]
pub struct QuectoVm {
registers: [i64; Register::Count as usize],
clock: usize,
}
impl QuectoVm {
#[hsrs::function]
pub fn new() -> Self {
/* ... */
}
#[hsrs::function]
pub fn store(&mut self, r: Register, v: i64) {
/* ... */
}
#[hsrs::function]
pub fn snapshot(&self) -> Point {
/* ... */
}
#[hsrs::function]
pub fn safe_div(&mut self, a: Register, b: Register) -> Result<i64, VmError> {
/* ... */
}
#[hsrs::function]
pub fn nonzero(&self, r: Register) -> Option<i64> {
/* ... */
}
}
}
生成的 Haskell
newtype Register = Register Word8
deriving (Eq, Show, Storable)
deriving (BorshSize, ToBorsh, FromBorsh) via Word8
pattern Reg0 :: Register
pattern Reg0 = Register 0
data Point = Point
{ pointX :: Int32
, pointY :: Int32
}
deriving (Generic, Eq, Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Point
data VmError = VmError
{ vmErrorCode :: Word32
}
deriving (Generic, Eq, Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct VmError
data QuectoVmRaw
newtype QuectoVm = QuectoVm (ForeignPtr QuectoVmRaw)
new :: IO QuectoVm
store :: QuectoVm -> Register -> Int64 -> IO ()
snapshot :: QuectoVm -> IO Point
safeDiv :: QuectoVm -> Register -> Register -> IO (Either VmError Int64)
nonzero :: QuectoVm -> Register -> IO (Maybe Int64)
许可证
MIT 或 Apache-2.0
相似文章
Show HN: Rust中基于伽罗瓦连接的可组合数值类型转换
一个Rust crate,实现了伽罗瓦连接,用于合法且可组合的数值类型转换,具有属性测试的不变性和编译时组合。
hax:一个Rust验证工具
hax是一个将Rust代码翻译成F*、Rocq和Lean等正式语言以进行高保障验证的工具。
Show HN:18KB 的 ls 替代工具,基于 no_std Rust 和 Libc
一个仅 18KB 的极简 ls 命令替代工具,使用 Rust 编写,基于 no_std 和 Libc。
Show HN: Nectar——一个类 Rust 的 React,编译为 WebAssembly
Nectar 是一个新型 Web 框架,将类 Rust 代码编译为 WebAssembly,通过 O(1) 信号更新和零依赖构建消除了 JavaScript 依赖。
Show HN: Bun-sqlgen – Bun的原始SQL类型安全库,无需ORM
Bun-sqlgen 是一个为 Bun 设计的类型安全的原始 SQL 库,它通过代码生成来检查查询是否匹配实时 Postgres 或 SQLite 模式,从而生成完全类型化且空安全的结果,无需 ORM。