ARTICLE DETAIL

资讯详情

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

Rust E0030 解析:为什么 `1000 ..= 5` 这样的范围模式会被 rustc 拒绝

Rust E0030 解析:为什么 `1000 ..= 5` 这样的范围模式会被 rustc 拒绝 Rust E0030 解析为什么1000 .. 5这样的范围模式会被 rustc 拒绝【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust本文讲解 Rust 编译错误E0030lower bound for range pattern must be less than or equal to upper bound的触发条件与底层判定机制当你在match中使用闭区间范围模式a .. b且下界大于上界时编译器会验证该区间非空并报错。读完后你将理解 rustc 在模式降级pattern lowering阶段如何用常量求值与比较逻辑判定区间是否为空、x .. x为何被允许并折叠为常量模式以及与开区间对应的E0579之间的区别。E0030 的规则范围模式必须是非空区间rustc 官方错误代码文档对E0030的定义见 E0030.md。其核心规则非常简短但明确When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range.即对范围模式做匹配时编译器会验证该区间非空。由于 Rust 的..范围模式同时包含两个端点非空条件等价于要求“下界小于或等于上界”start end。注意这里的比较符号是“小于或等于”——x .. x是合法的它恰好只匹配一个值。官方文档给出的错误代码示例match 5u32 { // This range is ok, albeit pointless. 1 .. 1 {} // This range is empty, and the compiler can tell. 1000 .. 5 {} }1 .. 1虽然“毫无用处”pointless但它是合法的非空区间而1000 .. 5是一个空区间编译器能确定地识别出来于是报错。编译期实际报长什么样仓库中的 UI 测试用例 E0030.rs 复现了最小触发场景fn main() { match 5u32 { 1000 .. 5 {} } }对应的 E0030.stderr 展示了精确的诊断输出error[E0030]: lower bound for range pattern must be less than or equal to upper bound -- $DIR/E0030.rs:3:9 | LL | 1000 .. 5 {} | ^^^^^^^^^^ lower bound larger than upper bound error: aborting due to 1 previous error For more information about this error, try rustc --explain E0030.要点错误级别是 hard errorerror[E0030]不是警告编译会中止主诊断消息为 “lower bound for range pattern must be less than or equal to upper bound”附加 label 指出 “lower bound larger than upper bound”精确标记整个范围表达式1000 .. 5提示可用rustc --explain E0030查看说明——该说明文本即来自错误代码文档的 teach 机制见下文teach字段。仓库中还有一个 E0030-teach.rs 测试用于验证--explain输出时诊断中的教学性 note 能够正确呈现。源码级原理E0030 在哪里、如何被触发诊断定义E0030的诊断结构体定义在 diagnostics.rs#[derive(Diagnostic)] #[diag(lower bound for range pattern must be less than or equal to upper bound, code E0030)] pub(crate) struct LowerRangeBoundMustBeLessThanOrEqualToUpper { #[primary_span] #[label(lower bound larger than upper bound)] pub(crate) span: Span, #[note( when matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range )] pub(crate) teach: bool, }几个值得注意的细节错误代码在#[diag(...)]宏属性中通过code E0030绑定note 文案与 E0030.md 的正文一致teach: bool字段控制是否输出这段教学性说明——当用户通过rustc --explain E0030触发 teach 模式时为true。触发点模式降级阶段报错的调用链位于模式从 HIR 降级为 THIR 的过程具体在 thir/pattern/mod.rs 的范围模式处理分支中let lo lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity); let hi lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); let cmp lo.compare_with(hi, ty, self.tcx); let mut kind PatKind::Range(Arc::new(PatRange { lo, hi, end, ty })); match (end, cmp) { // x..y where x y. (RangeEnd::Excluded, Some(Ordering::Less)) {} // x..y where x y. (RangeEnd::Included, Some(Ordering::Less)) {} // x..y where x y and x and y are finite. (RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() hi.is_finite() { let value ty::Value { ty, valtree: lo.as_finite().unwrap() }; kind PatKind::Constant { value }; } // x..y where x y, or x..y where x y. The range is empty error. _ { // Emit a more appropriate message if there was overflow. self.error_on_literal_overflow(lo_expr, ty)?; self.error_on_literal_overflow(hi_expr, ty)?; let e match end { RangeEnd::Included { self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { span, teach: self.tcx.sess.teach(E0030), }) } RangeEnd::Excluded if lo_expr.is_none() { self.tcx.dcx().emit_err(UpperRangeBoundCannotBeMin { span }) } RangeEnd::Excluded { self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanUpper { span }) } }; return Err(e); } }从这段源码结构看可以得出几个比文档更细的结论比较基于常量求值结果。两端点先经过lower_endpoint求值为PatRangeBoundary有限值、负无穷或正无穷再由compare_with得到Ordering。只有当区间确定为空闭区间x y或开区间x y时才报错——cmp为None如浮点 NaN 无法比较时不会在此处误报。x .. x被折叠为常量模式。源码中(RangeEnd::Included, Ordering::Equal)分支会把两端点相等的闭区间直接改写成PatKind::Constant这就是1 .. 1合法的原因——它等价于字面量模式1。错误分发按区间端点类型三分RangeEnd::Included..且lo hi→ 发出LowerRangeBoundMustBeLessThanOrEqualToUpper即E0030RangeEnd::Excluded..且lo hi不成立 → 发出LowerRangeBoundMustBeLessThanUpper即E0579开区间要求下界严格小于上界5 .. 5同样是空区间RangeEnd::Excluded且下界缺失形如..5但 5 已是类型最小值的特例→ 发出UpperRangeBoundCannotBeMin。溢出会优先提示更贴切的信息。空区间错误分支先调用两次error_on_literal_overflow若端点字面量本身溢出类型范围会先给出字面量溢出诊断避免误导用户去改区间顺序。teach: self.tcx.sess.teach(E0030)表明教学 note 的显隐由会话的 explain/teach 状态控制普通编译时只见主诊断与 label。区间边界比较的实现compare_with与边界表示定义在 thir.rs。PatRangeBoundary是一个三值枚举/// A (possibly open) boundary of a range pattern. /// If present, the const must be of a numeric type. pub enum PatRangeBoundarytcx { Finite(ty::ValTreetcx), NegInfinity, PosInfinity, }compare_with的实现要点同文件 第 1045 行起两个无穷边界同值时直接返回Equal0u8..与0u8..255描述同一区间的规范化前提对整型与char两端点源码专门做了“热路径”优化直接取标量叶子做无符号/有符号数值比较注释里提到unicode-normalization这类库有大量形如\u{037A}..\u{037F}的字符区间因此该路径被特殊加速浮点类型则通过rustc_apfloat恢复浮点语义后partial_cmp无法比较NaN时返回None从而不会误触发 E0030。同文件中还可见PatRange::contains与overlaps第 952–988 行复用了同一套比较逻辑来做成员判定和区间重叠判定——也就是说E0030 的检查只是这套“区间边界比较”基础设施在模式降级入口的一次应用。E0030 与相邻错误的边界理解 E0030 时容易与两个相邻诊断混淆结合 diagnostics.rs 与模式降级分支的代码可以厘清模式空区间条件触发错误x .. y闭区间x yE0030下界必须 ≤ 上界x .. y开区间x yE0579下界必须 上界LowerRangeBoundMustBeLessThanUpper.. y且y为类型最小值如u32的..0区间必然为空UpperRangeBoundCannotBeMin而x .. x两端点相等且有限不报错反而被降级为常量模式。这也解释了为什么 E0030 的比较符号是“less thanor equalto”——闭区间的端点相等时区间恰好包含一个元素仍是非空的。实用建议写范围模式前先确认类型方向1 .. 5、5u8..255这类区间在编译期即可被验证若需要“只匹配单个值”直接用字面量模式如1比1 .. 1更清晰虽然两者等价若报错提示下界大于上界先检查常量求值结果例如由const表达式计算的端点因为比较发生在常量求值之后若端点本身超出类型范围编译器会优先给出字面量溢出诊断此时应修的是字面量而非区间顺序。小结E0030是 rustc 在模式降级阶段对闭区间范围模式做的非空性静态验证编译器通过常量求值确定两端点用PatRangeBoundary::compare_with比较一旦发现x .. y满足x y便报出该错误x .. x则合法并被优化为常量模式。该机制的完整证据链可以在 E0030 文档、诊断定义、模式降级逻辑、边界比较实现 以及 UI 测试 中逐一查证。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表