E0433: cannot find html in pulldown_cmark
主线位置:输出主线的入口
push_html,它所在的html模块整个由 feature 控制。四个 feature 各开什么见怎么读这一卷的「feature 各打开什么」,那里已经贴过这条报错;本页讲它是怎么来的、为什么在工作区里会晚一步才出现。
flowchart TB
r["README 去掉命令行"] --> nd["default-features false"]
nd --> g["getopts 关了"]
nd --> h["html 也关了"]
h --> e["E0433 或 E0432"]
h --> ws{"工作区里"}
ws -->|别的成员开默认| ok["整体能编"]
ok --> p["单独 -p 时报错"]
e --> fix["features html"]
实验 crate:exp-pulldown-cmark/pitb-nodefault(default-features = false)、pitb-htmlonly(修法)、pitb-ws(两个成员的工作区)。M2 Max、macOS 27、rustc 1.99.0。
现象
README 的「Build options」一节说:
By default, the binary is built as well. If you don't want/need it, then build like this: ...
cargo add pulldown-cmark --no-default-features
库用户不需要命令行工具,照做,得到 pulldown-cmark = { version = "=0.13.5", default-features = false }。然后照 README 开头的「Example」写 pulldown_cmark::html::push_html(&mut html_output, parser):
error[E0433]: cannot find `html` in `pulldown_cmark`
--> src/bin/path.rs:3:19
|
3 | pulldown_cmark::html::push_html(&mut out, pulldown_cmark::Parser::new("*hello*"));
| ^^^^ could not find `html` in `pulldown_cmark`
|
note: found an item that was configured out
--> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pulldown-cmark-0.13.5/src/lib.rs:84:9
|
83 | #[cfg(feature = "html")]
| ---------------- the item is gated behind the `html` feature
84 | pub mod html;
| ^^^^
For more information about this error, try `rustc --explain E0433`.
写成 use pulldown_cmark::{html, Parser}; 时错误号换成 E0432,note 一样:
error[E0432]: unresolved import `pulldown_cmark::html`
--> src/main.rs:1:22
|
1 | use pulldown_cmark::{html, Parser};
| ^^^^ no `html` in the root
|
note: found an item that was configured out
...
rustc 的 note 已经指出了原因:html 模块被 cfg 掉了。
在工作区里,这条报错会晚到。 pitb-ws 有两个成员:a 照 README 写 default-features = false 并调用 push_html,b 普通地依赖 pulldown-cmark = "=0.13.5"。在工作区根目录 cargo build:
Compiling a v0.1.0 (~/exp-pulldown-cmark/pitb-ws/a)
Compiling b v0.1.0 (~/exp-pulldown-cmark/pitb-ws/b)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.29s
能编。单独编 a(cargo build -p a,或者把 a 发布出去、被别人依赖):
error[E0432]: unresolved import `pulldown_cmark::html`
error: could not compile `a` (bin "a") due to 1 previous error
原因
lib.rs 里 html 模块挂在 feature 上:
#[cfg(feature = "html")]
pub mod html;
Cargo.toml 的默认 feature 有两个:
default = ["getopts", "html"]
html = ["pulldown-cmark-escape"]
default-features = false 是一刀切:命令行工具要的 getopts 和库的 html 一起关掉。README 那句话只讲了前者。html 关掉以后 pulldown-cmark-escape 也不再是依赖,push_html、write_html_io、write_html_fmt 都不存在;解析器本身(Parser、Event、TextMergeStream)不受影响。
工作区里晚到,是因为 Cargo 的 feature 合并(feature unification):同一次构建里,同一个依赖只编一份,开的 feature 是所有请求的并集。b 要了默认 feature,a 拿到的 pulldown-cmark 里就有 html。只编 a 时没有 b 来补,问题才露出来。
「If you don't want/need it, then build like this: cargo build --no-default-features」来自 2017 年的 c224397「Remove unwanted conditional compilation」。那时默认 feature 只服务于命令行工具,这句话是对的。PR #519(8aab2f4「Make the html and escape modules optional」,首次发布在 0.10.0)把 default = ["getopts"] 改成 default = ["getopts", "html"],给 lib.rs 的 pub mod html 加上 #[cfg(feature = "html")],还把文档注释里的例子包进 # #[cfg(feature = "html")] { ... };README 的这一节没有动。后来 PR #904(1978ca3「chore(doc): use cargo add to instruct how to install this crate as dependency」,0.11.1)把旁边的 Cargo.toml 片段换成了 cargo add pulldown-cmark --no-default-features,意思没变。从 0.10.0 起,照这一节做的库用户都会丢掉 html。
修法
只想去掉 getopts,就把 html 写回来:
pulldown-cmark = { version = "=0.13.5", default-features = false, features = ["html"] }
pitb-htmlonly 用同样的 main.rs:
<p><em>hello</em></p>
cargo tree -e normal -f '{p} [{f}]',getopts 和它带来的 unicode-width 都不在了,html 和 escape crate 还在:
pitb-htmlonly v0.1.0 (~/exp-pulldown-cmark/pitb-htmlonly) []
└── pulldown-cmark v0.13.5 [html,pulldown-cmark-escape]
├── bitflags v2.13.2 []
├── memchr v2.8.3 [alloc,default,std]
├── pulldown-cmark-escape v0.11.0 []
└── unicase v2.10.0 []
开着默认 feature 时,getopts 作为依赖只是多编译一个小库,库代码不用它,不影响运行;嫌它多,就用上面这一行。要 SIMD 的话一起写进 features,见上一页开了 simd,ARM 上什么都没变。
下一个大版本要再看一次。 main 上的 Cargo.toml 已经是 default = ["std", "getopts", "html"],新增的 std feature 也在默认里,并多了一个 hashbrown。按 main 的 Cargo.toml 和 README,default-features = false 到那时还会关掉 std;README 的「Build options」那句话在 main 上仍然没改。本卷没有编译 main 验证升级后要怎么写。
怎么检查
- 看每个依赖实际开了什么:
cargo tree -e features -i pulldown-cmark,或者cargo tree -e normal -f '{p} [{f}]'。要调push_html的 crate,pulldown-cmark后面要有html。 - 在工作区里,每个成员单独编一次:
cargo build -p <成员>。别的成员的默认 feature 会掩盖这个问题,单独编、单独发布时才暴露。 - 写
default-features = false时把需要的 feature 列全。 对 0.13.5 来说,关掉默认 feature 会失去的公开 API 只有html模块;解析和事件流不受影响。
小结
- 0.13.5 的默认 feature 是
getopts和html。README 说用--no-default-features去掉命令行工具,html跟着关了,push_html报 E0433(路径写法)或 E0432(use写法),note指向#[cfg(feature = "html")]。 - README 这句写于 2017 年,那时默认 feature 只有命令行用的;0.10.0(PR #519)把
html加进默认后它就不对了。 - 修法:
default-features = false, features = ["html"]。 - 工作区里别的成员开着默认 feature 时,feature 合并会把问题藏起来,单独
-p构建才报错。