ARTICLE DETAIL

资讯详情

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

Rust 编译错误 E0203 深入解析:重复的 relaxed default bounds(`?Sized` 重复声明)

Rust 编译错误 E0203 深入解析:重复的 relaxed default bounds(`?Sized` 重复声明) Rust 编译错误 E0203 深入解析重复的 relaxed default bounds?Sized重复声明【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读E0203 是 rustc 在类型参数约束解析与 HIR 降级lowering阶段报出的编译错误含义为重复的放宽默认约束duplicate relaxed default bounds不被支持。本文以 rustc_error_codes 错误字典中 E0203 官方条目 为骨架结合 rustc 编译器源码中真正签发该错误的 rustc_ast_lowering 实现与 compiletest 用例讲清它是什么、在哪些场景触发、rustc 底层如何检测重复以及如何修复帮助你在阅读编译错误时快速定位根因。错误本体一条简短的硬错误E0203 对应的错误信息为Having duplicate relaxed default bounds is unsupported.存在重复的放宽默认约束是不受支持的。它是一条编译期硬错误hard error一旦触发代码无法通过编译。其语法结构可概括为同一个泛型参数或关联类型的 bound 列表中出现了两个相同的、带放宽修饰符?的约束例如连续写两个?Sized。触发场景原文档的官方示例rustc 错误字典中 E0203.md 给出了最小复现struct BadT: ?Sized ?Sized{ inner: T, }这里类型参数T的约束列表中?Sized出现了两次。Sized是泛型参数默认携带的 trait 约束而?Sized表示放宽relax该默认约束即允许T是不定长类型unsized type。当同一个默认约束被重复放宽时语义上存在冗余与歧义rustc 判定其不受支持并签发 E0203。值得注意的是该错误并不局限于struct的定义处。从源码的报错路径看凡是在泛型约束中被判定为重复放宽同一 trait的位置都会触发 E0203例如fn的泛型参数上同样适用见下文测试用例分析。语言机制背景什么是 relaxed default bound要理解 E0203需要先建立三个概念默认约束default bound在 Rust 中Sized是所有泛型类型参数的隐式约束——除非显式写出?Sized否则编译器默认T: Sized。这种无需用户书写、由语言自动施加的 trait 约束称为 default bound。除了Sized从编译器注释看未来还可能引入其他默认 trait如假设性的Move、Leak见 lib.rs 中的 FIXME 注释。放宽约束relaxed bound?Trait语法表示把对应的默认约束从泛型参数上移除。稳定通道上只有?Sized可用通过maybe_bound特性实现在more_maybe_bounds这一内部 gate 下其他默认 trait 的放宽才被允许见 validate_relaxed_bound 的注释。放宽默认约束的目的它让类型参数可以接受如[T]、str、dyn Trait等不定长类型是编写零拷贝切片处理、胖指针抽象、trait object 集合等代码的基础设施。E0203 只关心重复与单次放宽是否合法无关——单次?Sized是完全合法且常用的写法。源码级原理rustc 在哪一步、如何判定重复E0203 并非由类型检查阶段产生而是在抽象语法树AST向高层中间表示HIR的降级过程中签发。签发点在 compiler/rustc_ast_lowering/src/lib.rs 的validate_relaxed_bound方法约 L2431-L2462。调用链?修饰符从解析到校验当降级器处理一个多态 trait 约束时会调用lower_poly_trait_refL2407-L2429。它首先解析?修饰符对应的BoundPolarity一旦发现ast::BoundPolarity::Maybe(_)即出现?Sized这类放宽写法就调用validate_relaxed_bound进行合法性校验if let ast::BoundPolarity::Maybe(_) modifiers.polarity { self.validate_relaxed_bound(trait_ref, *span, rbp); }核心数据结构带去重表的RelaxedBoundPolicy校验的关键是当前放宽行为所处的位置策略RelaxedBoundPolicyL440-L455enum RelaxedBoundPolicya { /// 被放宽的 trait 的 DefId 及其出现位置 span。 Allowed(a mut FxIndexMapDefId, Span), Forbidden(RelaxedBoundForbiddenReason), }Allowed携带一张去重映射表key 是被放宽的 trait 的 DefIdvalue 是该放宽写法首次出现的源码位置Span。凡是在后续会进行 sized elaboration不定长展开的位置放宽默认约束rustc 都会以Allowed模式降级该区域例如 L1708、L1870、L2571 等多处以Allowed(mut Default::default())创建全新去重表。Forbidden用于 trait object 类型、supertrait 边界、trait 别名等不允许出现放宽写法的位置此时触发的是另一类relaxed bounds are not permitted错误而非 E0203。E0203 的精确签发逻辑当处于Allowed分支时validate_relaxed_bound执行如下检测L2447-L2462RelaxedBoundPolicy::Allowed(dedup_map) { // 解析失败等错误场景下 trait_def_id 才会返回 None let Some(trait_def_id) trait_ref.trait_def_id() else { return }; let tcx self.tcx; let err |s| { let name tcx.item_name(trait_def_id); tcx.dcx() .struct_span_err( vec![span, s], format!(duplicate relaxed {name} bounds), ) .with_code(E0203) .emit(); }; dedup_map.entry(trait_def_id).and_modify(|mut s| err(s)).or_insert(span); return; }这段代码的精妙之处在于通过trait_ref.trait_def_id()取得被放宽 trait 的唯一标识DefId用dedup_map.entry(trait_def_id)查询该 trait 此前是否已在本区域被放宽过若已存在and_modify分支把首次出现的位置s与当前出现的位置span一并作为错误 span 上报形成重复放宽Trait边界的诊断并with_code(E0203)挂上错误码若不存在or_insert(span)分支记录当前位置后静默通过。因此 E0203 的判定本质是以同一 trait 的 DefId 为键做集合去重第二次命中即报错。这也解释了为何重复的必须是同一个 trait——例如?Sized ?Iterator并不会触发 E0203但?只能应用于Sized的稳定性限制会另外报错因为它放松的是两个不同的默认约束。修复方法去掉重复的放宽写法修复的核心原则只有一个?Trait只写一次。针对原文档示例把两个?Sized合并为一个即可struct GoodT: ?Sized{ inner: T }推广到其他形态泛型函数fn fT: ?Sized ?Sized() {}改为fn fT: ?Sized() {}关联类型type T: ?Sized ?Sized;改为type T: ?Sized;形如fn fT: ?Sized() where T: ?Sized {}的参数列表 where 子句双重放宽同样会触发重复检测只需保留其中一处。如果你的本意是想表达多个互不相同的约束都放宽记住放宽的重复性按 trait 逐一判定写T: ?Sized SomeTrait无?是完全不同的场景不涉及 E0203。若确有其他默认 trait 需要放宽目前稳定版并无此能力无需在 stable Rust 中尝试。测试用例佐证compiletest 如何看待这一错误仓库中的 compiletest 用例 tests/ui/trait-bounds/duplicate-relaxed-bounds.rs 系统性地覆盖了 E0203 及相关语义可作为排查的对照手册fn dupesT: ?Sized ?Sized ?Iterator ?Iterator() {} //~^ ERROR duplicate relaxed Sized bounds //~| ERROR duplicate relaxed Iterator bounds //~| ERROR bound modifier ? can only be applied to Sized //~| ERROR bound modifier ? can only be applied to Sized该用例印证了以下关键事实同一泛型参数上相同 trait 的重复放宽会命中 E0203对Sized与Iterator各报一次用例注释明确指出早期 rustc 曾在关联类型上误报类型参数具有多个放宽默认边界如今已修正trait内的type Type: ?Sized ?Sized;同样按重复放宽正确报错见 L7-L11两个不同trait 的放宽如?Sized ?Iterator不再是 E0203 的管辖对象仅因?修饰符仅能作用于Sized的稳定限制而报错L25-L26where 子句 参数列表的组合重复同样会被 E0203 捕获说明去重表的作用域横跨同一泛型参数的完整声明L29-L34。与其他放宽边界错误的区分在阅读编译输出时很容易把 E0203 与相邻的放宽边界错误混淆此处给出判别表错误形态报错位置触发原因E0203 duplicate relaxed boundsAllowed区域同一 trait 被重复放宽本文主题relaxed bounds are not permitted in {位置}Forbidden区域在 trait object 类型、supertrait 边界、trait 别名等处写了?见RelaxedBoundForbiddenReasonL457-L466bound modifier?can only be applied toSized稳定性检查对非默认 trait 使用?稳定版限制this relaxed bound is not permitted here收尾兜底Forbidden分支中未被gate覆盖的情况L2508 起判别方法很简单看是不是同一个 trait 写了多遍?。是则走 E0203否则多半属于位置不允许或修饰符作用于错误目标。实战排查建议先数?个数报 E0203 时十有八九是复制粘贴泛型签名导致约束列表出现两遍相同写法直接删除重复项即可。注意 where 子句T: ?Sized若同时出现在尖括号内和where中同样属于重复放宽需合并位置。关注 span 提示rustc 的 E0203 诊断会同时标出该 trait第一次放宽与本次重复的位置后一个高亮往往就是你该删的地方。不要试图关闭该错误E0203 无对应的#[allow]它是语言层面的硬错误只能通过修改源码解决。总结E0203 是 rustc 在 AST→HIR 降级期由 validate_relaxed_bound 依据RelaxedBoundPolicy::Allowed去重表签发的一条硬错误语义上拒绝对同一默认约束如Sized的重复放宽。理解它的关键在于意识到?Trait是一条幂等语义应保持单次的声明重复不会增强放宽效果只会构成冗余并触发编译失败。删去多余的一次?Sized代码即可恢复合法。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表