关于

架构

核心层与三套绑定的分层、各自的实现取舍,以及不 panic 的设计约束。

适合:开发者

x-iztro 是一份 Rust 核心加三套绑定。算法只实现一次, Python、Go 与 C 调用方拿到的是同一份计算结果。

分层

              ┌──────────────────────────────┐
              │         Rust 核心库           │
              │  astro/  排盘、运限、宫位推算  │
              │  star/   安星                 │
              │  data/   枚举、常量、数据表    │
              │  i18n/   六语言词表与双向查找  │
              └──────────────┬───────────────┘

                    bridge.rs(编组与分派)
                    dto.rs(序列化契约)

        ┌────────────────────┼────────────────────┐
        │                    │                    │
   python.rs             wasm.rs               ffi.rs
   PyO3 扩展         wasm32-wasip1           C ABI
        │                    │                    │
   Python 包            Go 包(wazero)       C / C++ / 其他

两个共用层各司其职:

职责
bridge.rs入参解析、按名分派、结果编组。Python 与 Go 走同一个函数,行为没有分叉的余地。它是 crate 内部模块,不在公开 API 面上
dto.rs序列化契约:camelCase 键、按盘面语言翻译的值,外加 *Key 标识与排盘上下文

绑定文件因此很薄——只剩语言特有的部分:wasm 的内存协定、PyO3 的异常类型。

三套绑定的取舍

Python:PyO3 原生扩展

Rust 侧用 pythonize 在 Python 对象与 Rust 结构体之间直转, Python 侧用 dataclass 包装成类型化 API。

  • 编译为 abi3 wheel(abi3-py310),一个 wheel 覆盖 Python 3.10 及以上
  • 零运行期依赖,纯 stdlib(dataclasses + StrEnum)
  • 没有 JSON 序列化往返,开销最小

Go:内嵌 WebAssembly

编译为 wasm32-wasip1,用纯 Go 的 wazero 运行时执行。

选它而不是 cgo 的理由是保留 Go 的交叉编译能力:cgo 会让 GOOS/GOARCH 交叉编译变得极其麻烦,还要求使用者本机有 C 工具链。wasm 方案下 go get 即用, 静态链接与容器构建都不受影响。

单个 wasm 实例不能并发使用,包内维护一个实例池(上限 GOMAXPROCS): 每个调用取一个空闲实例,用完归还,多 goroutine 之间不串行化。

wasm 模块只编译一次,编译产物落盘缓存在 os.UserCacheDir() 下, 所以只有机器上第一次是 ~200ms,之后每个进程的首次调用 ~30ms。 iztro.Warmup(ctx) 可以把这段冷启动提前到服务启动阶段, iztro.Close(ctx) 归还全部实例内存。

wazero 的编译器后端只支持 amd64 与 arm64,其余架构走解释器,速度慢但结果相同。

每次调用另有一次 JSON 编解码与 wasm 内存拷贝,热路径上单次排盘在 0.5ms 量级。

C FFI

标准 C ABI,收 C 字符串、返回 JSON 字符串。 错误以 {"error":"..."} 返回,由 serde 生成以保证转义完备。 外层有 catch_unwind 兜底。

核心层不 panic

日期格式与存在性、公历年份范围、时辰索引在核心层校验,入口返回 Result; 性别、盘面语言、配置开关、标识这些以字符串传入的东西在绑定层解析时校验 (Rust 侧它们本来就是枚举)。两处都不 panic。 绑定层的 catch_unwind 只负责兜底库内部的缺陷,不承担参数校验职责。

这条约束由 wasm 目标决定

wasm 上 panic 会变成 trap,而 catch_unwind 在 wasm 上无效——兜不住。 更糟的是每次 trap 都会永久损耗模块实例的栈空间,累积之后连合法调用都会失败。 校验因此必须在更靠内的一层,三种编程语言共用同一道防线。

各语言的错误类型见 RustPythonGo 三页。

一致性怎么保证

三种编程语言的行为一致不靠纪律,靠三层结构约束:

算法只有一份 —— 全部计算在 Rust 核心完成,绑定层不含任何斗数逻辑
编组只有一份 —— Python 与 Go 调用同一个 bridge::query,入参解析与结果形状不可能分叉
断言成对 —— 每组对外能力在 Python 与 Go 两侧各有一组 parity 测试,断言同一张盘上的同一组取值

判断方法一律基于语言无关标识,因此同一条分析规则在三种编程语言上写出来、结果也相同。 标识约定见语言无关标识

版本

项目版本
对照的 iztrov2.5.8(版本锁定)
Rust edition2024
Python 要求3.10 及以上
Go 要求1.22 及以上

当前版本号见 crates.ioPyPI

许可

MIT。

本页目录