项目结构约定
用 duckfn 写的扩展项目有三个 crate root,规则只有一条:每个 crate root 都声明 mod extension;,谁也不要再导出谁。 做到了,模块嵌套到任意深度都成立,IDE 不再对 WebAssembly 那个
文件标红,命令行工具也能看见全部函数;做不到,模块第一次多出一层就会报 error[E0583]。
src/
├─ lib.rs mod extension; native,crate-type = ["cdylib"]
├─ wasm_lib.rs mod extension; wasm example,crate-type = ["staticlib"]
├─ bin/
│ └─ duckfn.rs #[path = "../extension/mod.rs"]
│ mod extension; CLI,cargo run --bin duckfn
└─ extension/
├─ mod.rs mod demo; mod functions; mod types;
│ duckfn_entrypoint!("my_ext");
├─ demo/
├─ functions/
└─ types/
三个 root 指向的都是 src/extension/mod.rs —— 一个目录模块,所以三者看到的是同一棵树,
duckfn_entrypoint! 也只出现在一个地方。
两个入口保持逐行一致
crate-type 无法按 target 区分,而两个 target 需要的值不同:本地编译要 cdylib,WebAssembly 要
staticlib。所以 Cargo.toml 多带了一个 crate root,注册成 example:
[[example]]
# crate-type can't be (at the moment) be overriden for specific targets
path = "src/wasm_lib.rs"
crate-type = ["staticlib"]
这个文件不是 src/lib.rs 的别名 —— 它自己声明 mod extension;。而官方上游模板写的是:
// src/wasm_lib.rs,官方模板
mod lib;
error[E0583] 就是从这来的。mod lib; 解析到 src/lib.rs,从这一刻起 lib.rs 就是一个文件
模块:它的子模块要去它旁边、也就是 src/lib/ 下面找。于是写在 src/lib.rs 里的 mod demo;
会去找 src/lib/demo.rs,而不是 src/extension/demo.rs,rustc 就报:
error[E0583]: file not found for module `demo`
--> src\lib.rs:3:1
error[E0583]: file not found for module `types`
--> src\lib.rs:4:1
扁平的 lib.rs 掩盖了这个问题,嵌套才把它暴露出来。两个 root 声明同一个路径就完全绕开了它 ——
src/extension/mod.rs 里的 mod demo; 会去找 src/extension/demo.rs 或
src/extension/demo/mod.rs。
这也是 src/lib.rs 与 src/wasm_lib.rs 各自只有三行的原因,以及为什么新增模块一律放进
src/extension/,而不是放在某个 crate root 旁边。
命令行工具是第三个 root
src/bin/duckfn.rs(用途见社区扩展文档页)同样是一个独立的
crate root,它的二进制里也必须带着同一棵树。它没法简单地依赖库,两个原因:
- 路径。
src/bin/下的 crate root 会把mod extension;解析成src/bin/extension.rs,而不是src/extension/。#[path]把它指回另外两个 root 用的那个目录模块。 - 注册。
#[duck_*]的文档元数据靠inventory的静态构造器收集,只有真正被链接进最终二进制的 目标文件才会生效。只依赖库的话,链接器可能把这些模块整块丢掉,导出的 CSV 会是空的 —— 而且不报错。
所以它自己声明这个模块:
#[path = "../extension/mod.rs"]
mod extension;
以后项目里再加 crate root,就是同样两行,指向 src/extension/mod.rs,别的都不用动。
IDE 对 src/wasm_lib.rs 标红
RustRover 或 rust-analyzer 给 src/wasm_lib.rs 标红,但 make debug / just build 都复现不出来。
因为 IDE 默认检查所有 target(cargo check --all-targets),于是把这个 example 也按本地平台
编译了一遍 —— 那并不是它被设计来编译的平台。给整个文件加架构门控即可,在其它 target 上它会被编译
成空:
#![cfg(target_arch = "wasm32")]
#![allow(special_module_name)]
mod extension;
第二个属性用于消除「crate root 不叫 lib.rs」那条 lint 提示。