ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Rust 编译器错误 E0627 详解:yield 表达式出现在协程字面量之外

Rust 编译器错误 E0627 详解:yield 表达式出现在协程字面量之外 Rust 编译器错误 E0627 详解yield 表达式出现在协程字面量之外【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读E0627 是 Rust 编译器rustc在类型检查阶段报出的错误代码含义是yield 表达式被使用在了协程字面量coroutine literal之外。本文以 rustc 源码仓库中的 E0627 错误代码文档 为骨架结合其触发点源码与 UI 测试用例系统讲解该错误的成因、复现方式、修复方法以及它与其他 yield 相关错误如特性门控错误的区别。读完本文你将能准确理解yield关键字的合法使用上下文并熟练写出正确的协程coroutine代码。一、错误概述E0627 是什么根据 E0627.md 的定义该错误的完整表述为A yield expression was used outside of the coroutine literal.即yield表达式只能出现在协程字面量内部。任何在普通函数、普通闭包、常量或静态变量等非协程上下文中使用yield的代码都会触发该错误。rustc 会同时在终端输出一行简短提示error[E0627]: yield expression outside of coroutine literal二、错误示例在普通函数中使用 yield下面是文档中给出的官方错误示例compile_fail测试用例#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn fake_coroutine() - static str { yield 1; return foo } fn main() { let mut coroutine fake_coroutine; }这段代码的意图显然是假装写一个协程在一个普通具名函数fake_coroutine内部使用了yield 1。然而具名函数不是协程字面量yield在函数体内没有任何合法的挂起语义因此编译器拒绝编译。该示例在仓库的 UI 测试中也有对应的最小化版本 tests/ui/coroutine/yield-in-function.rs#![feature(coroutines)] fn main() { yield; } //~^ ERROR yield expression outside //~| ERROR yield can only be used in其期望输出文件 tests/ui/coroutine/yield-in-function.stderr 展示了真实的编译诊断error: yield can only be used in #[coroutine] closures, or gen blocks -- $DIR/yield-in-function.rs:3:13 | LL | fn main() { yield; } | ^^^^^ | help: use #[coroutine] to make this closure a coroutine | LL | #[coroutine] fn main() { yield; } | error[E0627]: yield expression outside of coroutine literal -- $DIR/yield-in-function.rs:3:13 | LL | fn main() { yield; } | ^^^^^ error: aborting due to 2 previous errors可以看到一次非法使用yield会同时产生两条错误一条是特性门控feature gate检查给出的提示yield只能用于#[coroutine]闭包或gen块另一条才是 E0627 本身。三、错误成因yield 为什么必须限定在协程字面量内3.1 yield 的本质协程的挂起点在 rustc 的 HIR 定义中yield被明确定义为协程的挂起点。见 compiler/rustc_hir/src/hir.rs 中ExprKind枚举的注释A suspension point for coroutines (i.e.,yield expr).也就是说yield表达式的语义是将当前执行挂起并把expr的值产出给调用方等待下一次 resume。这种挂起/恢复机制只有在一个被降级为协程状态机的闭包或块内才有意义。普通函数是一次性执行到底的没有挂起机制因此yield在其中没有合法含义。3.2 源码触发点check_expr_yieldE0627 在类型检查阶段被触发。类型检查器在处理yield表达式时调用check_expr_yield其实现位于 compiler/rustc_hir_typeck/src/expr.rsfn check_expr_yield( self, value: tcx hir::Exprtcx, expr: tcx hir::Exprtcx, ) - Tytcx { match self.coroutine_types { Some(CoroutineTypes { resume_ty, yield_ty }) { self.check_expr_coercible_to_type(value, yield_ty, None); resume_ty } _ { self.dcx().emit_err(YieldExprOutsideOfCoroutine { span: expr.span }); // Avoid expressions without types during writeback (#78653). self.check_expr(value); self.tcx.types.unit } } }从源码结构看其判定逻辑非常清晰当类型检查上下文带有coroutine_types即当前正处于协程字面量内部时yield的产出值会被强制转换为协程的yield_ty整个yield表达式的类型是resume_ty当coroutine_types为None即当前不在任何协程字面量内时直接通过emit_err发出 E0627 诊断并把表达式类型临时设为()。值得注意的是源码中的一行注释// Avoid expressions without types during writeback (#78653).它对应测试 tests/ui/coroutine/yield-outside-coroutine-issue-78653.rs该测试还同时验证了yield右侧表达式会继续被检查、以及{integer}不是迭代器等附加错误。这说明即使yield本身非法编译器仍会继续检查其子表达式以避免回写writeback阶段出现表达式无类型的级联崩溃——这是错误恢复error recovery机制的一部分。3.3 诊断结构体定义E0627 对应的诊断结构体定义在 compiler/rustc_hir_typeck/src/diagnostics.rs#[derive(Diagnostic)] #[diag(yield expression outside of coroutine literal, code E0627)] pub(crate) struct YieldExprOutsideOfCoroutine { #[primary_span] pub span: Span, }rustc 采用结构化的#[diag(...)]声明式诊断体系这里的code E0627把错误代码与rustc_error_codes中的 E0627.md 文档一一对应起来。当用户运行rustc --explain E0627时编译器展示的正是该文档的正文。四、修复方法正确构造协程字面量文档给出的正确示例是使用#[coroutine]属性修饰一个闭包字面量#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn main() { let mut coroutine #[coroutine] || { yield 1; return foo }; }修复要点说明把普通函数改为闭包具名函数无法被标记为协程必须使用闭包字面量为闭包添加#[coroutine]属性该属性将闭包显式标记为协程使其内部可以使用yield保留特性开关协程目前仍是实验特性需要在 crate 顶部声明#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]变量需要mut协程被调用时其内部状态会发生改变挂起/恢复因此绑定需要可变性。修正后yield 1表示协程第一次被 poll 时挂起并产出1yield_ty为i32恢复后继续执行return fooreturn_ty为static str。4.1 其他合法的 yield 上下文根据 rustc 的特性门控实现见 compiler/rustc_ast_passes/src/feature_gate.rs 附近对coroutines/gen_blocks与yield_expr关系的注释目前yield合法的上下文还包括gen块生成器块generator block同样为实验特性feature gate 声明在 compiler/rustc_ast_passes/src/feature_gate.rsgate_all!(gen_blocks, gen blocks are experimental)async块/闭包内部async上下文中yield是合法的对应 HIR 中CoroutineKind为 async 的协程但异步协程的yield_ty为()通常配合gen块形成异步生成器可参考 tests/ui/coroutine/async-gen-deduce-yield.rs 等测试。需要强调的是普通具名函数、const块、static初始化、普通闭包都不属于协程字面量。仓库测试 tests/ui/coroutine/yield-in-const.stderr、tests/ui/coroutine/yield-in-static.stderr 以及 issue 91477tests/ui/coroutine/issue-91477.rs分别验证了这些场景下的报错行为。五、E0627 与相关错误的辨析在排查 yield 相关编译错误时需要注意区分以下两个常一起出现的错误错误阶段含义E0627类型检查HIR typeckyield出现在协程字面量之外属于上下文合法性错误特性门控错误无错误码AST 阶段feature gateyield相关特性未启用或yield被用在完全不允许的位置此时文本提示为 yieldcan only be used in#[coroutine]closures, orgenblocks并带有添加#[coroutine]的 help 建议正如 tests/ui/coroutine/yield-in-function.stderr 所示在未启用特性时于普通函数中使用yield会先由 AST 阶段的特性检查给出只能在#[coroutine]闭包或gen块中使用的提示随后由类型检查阶段给出 E0627。二者分工不同前者校验语法位置与特性开关后者校验类型层面的协程上下文。此外即使yield位于协程字面量内部也可能触发其他错误码例如借用检查阶段的跨 yield 借用cannot_borrow_across_coroutine_yield见 compiler/rustc_borrowck/src/borrowck_errors.rs这类问题不在 E0627 的范围内。六、快速定位与调试建议当你在自己的代码中看到 E0627 时可以按以下步骤排查定位 yield 位置检查报错 span 指向的yield表达式确认它所在的最小代码单元确认上下文该yield是否位于#[coroutine]闭包或gen块实验特性内部如果不在把外层结构改造成协程字面量将具名函数改写为#[coroutine]闭包或将块改写为gen块确认特性声明crate 顶部是否声明了#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]使用gen块时还需要#![feature(gen_blocks)]使用 explain 查看官方说明直接运行rustc --explain E0627或在错误输出中查看For more information about this error, try rustc --explain E0627.的指引对照仓库测试可参考 tests/ui/coroutine/ 目录下大量协程相关测试如 yield-in-function.rs、yield-outside-coroutine-issue-78653.rs理解各种合法与非法场景的期望行为。七、总结E0627 是 rustc 在类型检查阶段用于保护协程语义的错误代码它确保yield表达式只出现在协程字面量内部。其底层实现位于 compiler/rustc_hir_typeck/src/expr.rs 的check_expr_yield诊断定义与错误文档则分别位于 diagnostics.rs 与 E0627.md。修复该错误的核心手段只有一个把使用yield的代码放进#[coroutine]闭包或gen块这样的协程字面量中并正确声明实验特性开关。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表