news 2026/9/10 13:04:40

Rust 错误 E0789 详解:`rustc_allowed_through_unstable_modules` 必须与 `[stable]` 成对出现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust 错误 E0789 详解:`rustc_allowed_through_unstable_modules` 必须与 `[stable]` 成对出现

Rust 错误 E0789 详解:rustc_allowed_through_unstable_modules必须与#[stable]成对出现

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

本篇文章深入解析 rustc 编译器内部错误码E0789的触发条件、底层实现与修复方法。该错误与编译器内部属性#[rustc_allowed_through_unstable_modules]密切相关,仅在 rustc 编译器与标准库自身使用#[stable]/#[unstable]稳定性标记(staged API)的场景中出现,普通用户代码不会触发。读完本文,你将理解该属性在稳定性检查体系中的定位、E0789 的产生链路,以及如何正确配对属性避免该错误,并能结合仓库源码与测试用例验证其行为。

错误码 E0789 是什么

E0789 是 rustc 编译器内部错误码,其完整诊断信息定义在 compiler/rustc_error_codes/src/error_codes/E0789.md 中。该文档开篇明确说明:

This error code is internal to the compiler and will not be emitted with normal Rust code.

E0789 是编译器内部错误,普通 Rust 代码不会触发它。它只在 rustc 自身与标准库(standard library)的构建过程中,当稳定性属性使用不当时才会被报告。

该错误的本质是:内部属性rustc_allowed_through_unstable_modules必须被用在带有#[stable]属性的条目(item)上,否则编译器就报出 E0789。

触发场景:稳定性属性的配对关系

staged API 与稳定性标记体系

要理解 E0789,首先需要了解 Rust 的 staged API(分阶段 API)机制。标准库和编译器内部通过#[stable(feature = "...", since = "...")]#[unstable(feature = "...", issue = "...")]属性来管理 API 的稳定性,这依赖于staged_api特性。编译器内部属性(以rustc_前缀开头)则依赖rustc_attrs特性,这些特性都是 perma-unstable(永久不稳定)的,绝不允许在编译器与标准库之外使用

这些内部属性在 compiler/rustc_feature/src/builtin_attrs.rs 中注册,rustc_allowed_through_unstable_modulesstaged_apirustc_attrs同属内部属性列表,并在 compiler/rustc_attr_ir/src/data_structures.rs 中被统一表示为Stability这一属性种类——该注释明确写着它同时代表#[stable]#[unstable]#[rustc_allowed_through_unstable_modules],可见三者共享同一套稳定性数据结构。

稳定的条目不能出现在不稳定模块中

默认情况下,当一个条目带有#[stable]属性时,包裹它的模块也必须带有#[stable]属性,否则该条目会变成de facto(事实上)不稳定的——因为用户要使用它,就必须先经过一个不稳定的路径。

#[rustc_allowed_through_unstable_modules]正是为解决这一问题而存在的变通方案(workaround):它允许一个已稳定的条目"逃逸"(escape)出其不稳定的父模块。典型场景是历史上一些条目(如core::intrinsics::transmute)在路径稳定性检查加入之前就"意外地"通过不稳定路径被稳定化,为保持向后兼容而使用该属性放行。

E0789 的产生:缺少配对的#[stable]

E0789 在该属性单独出现、没有配套的#[stable]属性时被触发。下面给出仓库文档中的原始错误示例(注意其中#[stable]被注释掉了):

// NOTE: both of these attributes are perma-unstable and should *never* be // used outside of the compiler and standard library. #![feature(rustc_attrs)] #![feature(staged_api)] #![allow(internal_features)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] #[rustc_allowed_through_unstable_modules( message = "deprecation message", module = "stable_module", )] // #[stable(feature = "foo", since = "1.0")] struct Foo; // ^^^ error: `rustc_allowed_through_unstable_modules` attribute must be // paired with a `stable` attribute

编译这段代码会得到:

error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute

其中messagemodule参数是必需的,分别表示"迁移提示文案"与"建议用户迁移到的新模块路径",具体规则见下文源码解析。

源码级解析:错误在哪里被抛出

1. 属性解析阶段:收集并校验配对

E0789 的实际抛出处位于 compiler/rustc_attr_parsing/src/diagnostics.rs:

#[derive(Diagnostic)] #[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, }

而触发该诊断的逻辑在 compiler/rustc_attr_parsing/src/attributes/stability.rs 的finalize阶段:

fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> { if let Some(atum) = self.allowed_through_unstable_modules { if let Some(( Stability { level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, .. }, _, )) = self.stability { *allowed_through_unstable_modules = Some(atum); } else { cx.dcx() .emit_err(diagnostics::RustcAllowedUnstablePairing { span: cx.target_span }); } } // ... }

从源码结构可以清晰看到校验逻辑:

  • 属性解析器首先收集条目上出现的rustc_allowed_through_unstable_modules参数(atum);
  • finalize阶段,检查该条目是否同时带有#[stable]属性(即StabilityLevel::Stable);
  • #[stable]:把(message, module)二元组写入稳定级中保存(*allowed_through_unstable_modules = Some(atum)),配对成功;
  • 没有#[stable]:立即通过dcx().emit_err(...)抛出 E0789 错误。

2. 参数解析规则:messagemodule缺一不可

在同一文件的 属性解析模板 中,该属性被定义为列表形式message = "...", module = "..."

( &[sym::rustc_allowed_through_unstable_modules], template!(List: &[r#"message = "...", module = "..."#]), unstable!(staged_api), |this, cx, args| { // 依次解析 name-value 对 match name.name { sym::message => { /* 记录 message */ } sym::module => { /* 记录 module */ } _ => { cx.adcx().expected_specific_argument(name.span, &[sym::message, sym::module]); } } // ... let allowed_through_unstable_modules = try { (message?, module?) }; if allowed_through_unstable_modules.is_none() { cx.emit_err(diagnostics::RustcAtumMissingParams { span: cx.attr_span }); } this.allowed_through_unstable_modules = allowed_through_unstable_modules; }, ),

关键约束(均有源码依据):

  • messagemodule两个参数都是必需的:任何一个缺失,try { (message?, module?) }求值失败,会报出RustcAtumMissingParams诊断("rustc_allowed_through_unstable_modulesattribute must havemessageandmoduleparams",定义在 diagnostics.rs);
  • 不允许重复键:同一个键出现两次会触发duplicate_key诊断;
  • 不允许未知键:出现message/module之外的键会触发expected_specific_argument诊断;
  • 该属性本身依赖staged_api特性门控(unstable!(staged_api))。

3. 路径检查阶段:运行时的放行逻辑

配对成功后,该属性在稳定性检查 pass 中发挥作用。核心逻辑位于 compiler/rustc_passes/src/stability.rs 的visit_path

  • 当路径指向的条目本身是稳定的(item_is_allowed),编译器会进一步检查路径的父模块段是否稳定(path.segments.iter().rev().skip(1),跳过最后一段避免重复检查);
  • 如果该条目带有allowed_through_unstable_modules(即配对成功的(message, module)),则对父模块段改用eval_stability_allow_unstable直接评估(stability.rs);
  • 若评估结果为不允许(如模块已迁移或用户路径不对),则发出弃用(deprecated)警告,并借助RustcAtumSuggestion诊断给出迁移提示,引导用户从module参数指定的新路径导入。

源码注释也直接印证了该属性的历史定位(stability.rs):

We include special cases via#[rustc_allowed_through_unstable_modules]for items that were accidentally stabilized through unstable paths before this check was added, such ascore::intrinsics::transmute.

即:该属性是给"在此检查加入之前、通过不稳定路径被意外稳定化"的条目(例如core::intrinsics::transmute)保留的特殊例外。

正确用法与修复方式

修复 E0789:补上#[stable]属性

修复方式即是为条目同时标注#[stable]#[rustc_allowed_through_unstable_modules]

#![feature(rustc_attrs)] #![feature(staged_api)] #![allow(internal_features)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] #[stable(feature = "foo", since = "1.0")] #[rustc_allowed_through_unstable_modules( message = "deprecation message", module = "stable_module", )] struct Foo;

注意顺序与要点:

  • #[stable]是前提,messagemodule参数必须同时给出;
  • module参数填写建议用户迁移到的新稳定模块路径(字符串形式);
  • message参数填写展示给用户的迁移提示文案
  • 该属性只能在编译器与标准库内部使用(依赖 perma-unstable 的rustc_attrsstaged_api特性)。

仓库中的真实使用范例

仓库测试用例 tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs 给出了该属性的标准写法:

#![crate_type = "lib"] #![feature(staged_api)] #![feature(rustc_attrs)] #![stable(feature = "stable_test_feature", since = "1.2.0")] #[unstable(feature = "unstable_test_feature", issue = "1")] pub mod unstable_module { #[stable(feature = "stable_test_feature", since = "1.2.0")] #[rustc_allowed_through_unstable_modules( message = "use the new path instead", module = "stable", )] pub trait OldStableTraitAllowedThoughUnstable {} #[stable(feature = "stable_test_feature", since = "1.2.0")] pub trait NewStableTraitNotAllowedThroughUnstable {} }

对比之下,同模块内未加该属性的NewStableTraitNotAllowedThroughUnstable在从外部通过不稳定路径导入时,会被正常拦截并报出E0658: use of unstable library feature

行为验证:测试用例与预期输出

仓库的编译测试 tests/ui/stability-attribute/allowed-through-unstable.rs 用//@ aux-build机制构建上述辅助 crate,然后分别导入两个 trait:

extern crate allowed_through_unstable_core; use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; //~WARN use of deprecated import through accidentally stabilized module `unstable_module` use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`

预期的.stderr输出(见 allowed-through-unstable.stderr)精确展示了两种行为的差异:

  1. 该属性的OldStableTraitAllowedThoughUnstable:只产生一条warning(默认#[warn(deprecated)],提示"use of deprecated import through accidentally stabilized module",并给出help: use the new path instead的迁移建议;
  2. 不带该属性的NewStableTraitNotAllowedThroughUnstable:直接产生error E0658(使用不稳定特性unstable_test_feature),编译失败。

这一对照充分说明:rustc_allowed_through_unstable_modules的效果是把"硬性错误(E0658)"降级为"可修复的弃用警告",同时保留对历史路径的兼容,并通过module参数引导用户迁移到新路径。

相关错误码与延伸阅读

  • E0658use of unstable library feature,即未放行时从不稳定路径访问稳定条目会触发的错误(同一测试用例中有对照输出);
  • E0717rustc_promotable属性必须与rustc_const_unstable/rustc_const_stable配对,属于同类的"内部属性配对"校验(定义于 diagnostics.rs);
  • 稳定性检查的完整 pass 位于 compiler/rustc_passes/src/stability.rs,路径检查与放行逻辑集中在其visit_path实现中;
  • 完整的错误码文档目录位于 compiler/rustc_error_codes/src/error_codes/,可用rustc --explain E0789在本地查看该错误说明。

小结

  • E0789 是编译器内部错误,普通用户代码不会触发;它只在 rustc 与标准库内部,当#[rustc_allowed_through_unstable_modules]#[stable]配对缺失时出现;
  • 该属性的作用是允许稳定条目从"意外稳定化"的不稳定父模块路径中被访问,并把硬错误降级为带迁移提示的弃用警告;
  • 修复方式是为条目同时标注#[stable]#[rustc_allowed_through_unstable_modules(message = "...", module = "...")],两个参数缺一不可;
  • 源码证据链完整:属性注册在 builtin_attrs.rs,配对校验在 attributes/stability.rs,诊断定义在 diagnostics.rs,路径放行逻辑在 rustc_passes/src/stability.rs,并有 tests/ui/stability-attribute/allowed-through-unstable.rs 系列测试用例验证其行为。

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!