Rust 编译器错误 E0071 详解:结构体字面量语法不能用于非结构体类型
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
Rust 编译器错误 E0071 出现在使用结构体字面量(struct-literal)语法初始化一个并非结构体、枚举变体或联合(union)的项时。本文基于 Rust 官方错误码文档 E0071.md 展开,结合类型检查器中实际发出该诊断的源码(checks.rs)与官方 UI 测试用例,讲解 E0071 的触发条件、完整诊断输出、两种标准修复方式,以及编译器判定“结构体路径”背后的实现机制。
错误定义:何时会触发 E0071
根据错误码文档,E0071 的定义是一句话:
A structure-literal syntax was used to create an item that is not a structure or enum variant.
即:使用结构体字面量语法创建了并非结构体或枚举变体的项。典型示例如下(该示例来自原文档,标记为compile_fail,E0071,表示预期编译失败):
type U32 = u32; let t = U32 { value: 4 }; // error: expected struct, variant or union type, // found builtin type `u32`这里U32只是内置类型u32的别名。u32是基本类型(builtin type),没有字段,自然无法通过U32 { value: 4 }这样的字面量形式“构造”。编译器会在类型检查阶段报告:
error[E0071]: expected struct, variant or union type, found builtin type `u32`诊断信息固定为expected struct, variant or union type, found {},其中{}处填充实际解析到的类型名。这个格式可以在源码中直接看到,struct_span_code_err!宏在发出 E0071 时使用了该模板(见 checks.rs 第 1119–1127 行),并在路径上附加not a struct的 span 标签。
两种标准修复方式
原文档给出的修复原则是:确认名称拼写正确,且使用了正确形式的初始化器。针对上面的示例,文档给出了两种修法,均完整继承如下。
方式一:改用普通字面量初始化别名
如果别名指向的是基本类型,直接用值初始化即可:
type U32 = u32; let t: U32 = 4;类型别名不改变底层类型的本质——U32就是u32,因此初始化它只需要一个u32字面量,字段形式在此毫无意义。
方式二:把别名定义为一个真正的结构体
如果本意就是想要一个带value字段的新类型,则应该定义结构体而非别名:
struct U32 { value: u32 } let t = U32 { value: 4 };此时U32是一个真实存在的结构体,U32 { value: 4 }是合法的结构体字面量,编译通过。
这两种方式代表了两种不同的意图:前者是“误用了字面量语法”,后者是“误用了别名语法”。诊断出真实意图后,选对应的一条路径修改即可。
真实诊断输出:结合官方测试用例
仓库中的 UI 测试 E0071.rs 覆盖了文档示例之外的一类典型场景——别名指向一个枚举(该枚举根本没有可被字面量构造的变体):
enum Foo {} type FooAlias = Foo; fn main() { let u = FooAlias { value: 0 }; }对应的期望诊断输出 E0071.stderr 展示了 E0071 的完整格式:
error[E0071]: expected struct, variant or union type, found `Foo` --> $DIR/E0071.rs:5:13 | LL | let u = FooAlias { value: 0 }; | ^^^^^^^^ not a struct error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0071`.从中可以确认三个实战要点:
- 错误位置定位在**结构体字面量的路径(path)**上,而不是整个表达式;
- span 标签固定为
not a struct; - 结尾附带
rustc --explain E0071提示,可通过它查询该错误码的完整说明(即本文依据的 E0071.md)。
源码级机制:编译器如何判定一个路径“不是结构体”
E0071 的发出点位于类型检查(typeck)阶段。具体实现在 check_struct_path 函数(compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs第 1069 行起),该函数负责解析结构体字面量中的路径并验证其是否指向合法的可构造项。其判定逻辑可以归纳为三步。
第一步:按 DefKind 分类路径目标
check_struct_path先调用finish_resolving_struct_path完成路径解析,得到Res与归一化后的类型ty,然后按定义的种类(DefKind)分派:
DefKind::Variant(枚举变体):要求该类型的 ADT 定义(ty_adt_def())存在,取出对应变体返回;DefKind::Struct、DefKind::Union、DefKind::TyAlias、DefKind::AssocTy,以及自类型参数/别名:要求归一化后的类型存在 ADT 定义,且!adt.is_enum(),即别名必须最终指向非枚举的 ADT(结构体或联合),并取其non_enum_variant();- 其他定义种类会直接触发编译器内部的
bug!,说明这些情况在更早阶段已被拦截。
注意这里对TyAlias的处理与文档示例的对应关系:type U32 = u32虽然属于DefKind::TyAlias,但归一化后u32不是 ADT(ty_adt_def()返回None),于是落入None分支,即“无法构造”的失败路径。
第二步:成功时注册类型注解与约束
当路径合法时,函数还会注册用户类型注解(write_user_type_annotation_from_args),并对路径中使用的类型参数添加必要的约束义务(add_required_obligations_for_hir),再返回(variant, ty)供后续字段检查使用。
第三步:失败时发出 E0071,但有例外抑制
失败分支并非无条件报错。源码中有一个值得注意的细节(checks.rs 第 1111–1128 行):
Err(match *ty.normalized.kind() { ty::Error(guar) => { // E0071 might be caused by a spelling error, which will have // already caused an error message and probably a suggestion // elsewhere. Refrain from emitting more unhelpful errors here // (issue #88844). guar } _ => struct_span_code_err!( self.dcx(), path_span, E0071, "expected struct, variant or union type, found {}", ty.normalized.sort_string(self.tcx) ) .with_span_label(path_span, "not a struct") .emit(), })从源码注释可以确认:如果类型本身已经是错误类型(ty::Error),说明拼写错误等先前错误已经产生过诊断,此处不再重复发出 E0071,以避免在同一处堆叠无用的报错(注释中引用了 issue #88844)。只有在类型解析“看似成功但确实不是 ADT”时,才会真正发出 E0071。
补充:E0163 已并入 E0071
错误码注册文件中保留了一条历史注释(lib.rs 第 576 行):
// E0163, // merged into E0071可以据此确认:早期 Rust 中独立的错误码 E0163 已被合并进 E0071。如果你在较老的教程或资料中看到 E0163,其对应的场景如今统一由 E0071 报告。
排查清单
遇到 E0071 时,可以按以下顺序自查:
- 名称拼写:确认路径指向的确实是想要的那个类型。若是拼写错误导致解析到了别的项,可能先前的错误已提示过,E0071 会被抑制不再出现;
- 别名是否指向基本类型(如文档中的
type U32 = u32):改为普通字面量初始化,即let t: U32 = 4;; - 别名是否指向枚举(如测试用例中的
type FooAlias = Foo):枚举必须通过具体变体构造(Foo::SomeVariant形式),不能对整个枚举使用结构体字面量; - 是否本想定义结构体:若确实需要一个带字段的新类型,用
struct定义真实结构体,再使用字面量构造; - 使用
rustc --explain E0071查看完整错误说明,与本文依据的官方错误码文档内容一致。
小结
E0071 是 Rust 类型检查阶段针对“结构体字面量语法用错了目标”而发出的错误,核心信息为expected struct, variant or union type, found {}。官方文档(E0071.md)给出了别名指向u32的经典反例与两种修复方式;实现上,该诊断由 check_struct_path 在路径归一化后无法得到合法 ADT 变体时发出,并对已由拼写错误等产生过诊断的情况做了抑制以避免重复报错;官方 UI 测试(E0071.rs 与 E0071.stderr)则补充覆盖了“别名指向枚举”的场景并固化了诊断输出格式。理解这条“路径解析 → 类型归一化 → ADT 判定 → 诊断发出”的链路,有助于在遇到 E0071 时快速区分是拼写问题、别名误用还是结构体定义缺失。
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考