ARTICLE DETAIL

资讯详情

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

Rust 编译错误 E0033 详解:trait 对象指针为什么不能被模式隐式解引用

Rust 编译错误 E0033 详解:trait 对象指针为什么不能被模式隐式解引用 Rust 编译错误 E0033 详解trait 对象指针为什么不能被模式隐式解引用【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustE0033 是 rustc 在类型检查阶段发出的一类错误含义是“一个 trait 类型被解引用了”A trait type has been dereferenced当你试图用let x ...这样的模式把 trait 对象指针“拆包”、把指向的值绑定到一个本地变量时编译器会拒绝该操作。本文基于 rustc 仓库中的官方错误码文档 E0033.md结合 rustc_hir_typeck 中真正发出该错误的源码实现讲解错误的成因、触发条件与正确的规避写法。E0033 错误码的背景在 rustc 中所有错误码集中维护在 rustc_error_codes 这个 crate 里源码文件开头注明每个错误码的说明文档都放在error_codes/EXXXX.md文件中且必须遵循 RFC 1567错误码解释文档规范化所定义的格式文档内容还会被 tidy 工具检查。E0033 正是通过 lib.rs 中的error_codes!宏注册的在册错误码之一宏中可见0033条目。这套文档还有一个实用入口编译报错时可以用rustc --explain E0033打印对应的解释文本。该文本直接来自本文所依据的说明文档源码中通过sess.teach(err.code)分支注入见下文。因此理解 E0033.md 就是理解编译器“教你”的那段话。出错的代码示例官方文档给出的最小复现示例如下trait SomeTrait { fn method_one(self) {} fn method_two(self) {} } implT SomeTrait for T {} fn main() { let trait_obj: SomeTrait some_value; // 这行会触发 E0033试图隐式解引用创建一个无固定大小的本地变量 let invalid trait_obj; // 正确做法不绑定值直接通过指针调用方法 trait_obj.method_one(); trait_obj.method_two(); }仓库原文档中的示例带有compile_fail,E0033属性用于测试框架确认该代码确实编译失败并给出 E0033。问题出在let invalid trait_obj;这一行。invalid是一个解引用模式它要求把trait_obj所指向的值取出绑定给本地变量invalid。但trait_obj的类型是SomeTrait它指向的值是一个trait 对象而 trait 对象没有编译期已知的尺寸——这正是错误被触发的根本原因。为什么会报错trait 对象是动态大小类型E0033.md 的核心解释只有一句话却点破了 Rust 类型系统的底层约束指向 trait 类型的指针不能被模式隐式解引用。每个 trait 都定义了一个类型但由于 trait 实现者的尺寸不固定这个类型没有编译期尺寸。因此对 trait 类型的任何访问都必须通过指针进行。展开来说本地变量必须有确定的尺寸。Rust 中一个栈上的let绑定对应一段确定大小的存储编译器必须知道该变量占多少字节。trait 对象dyn Trait/Trait/BoxTrait是动态大小类型DST。SomeTrait的所有实现者可以是 1 字节的u8也可以是 128 字节的结构体SomeTrait只保存了数据指针与 vtable 指针本身并不包含实现者的完整数据。因此let invalid trait_obj;等价于要求编译器生成一个“尺寸未知”的栈变量invalid这在语言层面不可能完成编译器只能报错。结论对 trait 类型的访问必须始终通过指针dyn Trait、Boxdyn Trait等进行解引用deref只能用于“借用引用”或“方法调用”这类不产生新存储的场合而不能用于把值绑定出来。这一点在 Rust Reference 的 Trait Objects 章节中有系统论述E0033 文档末尾即引导读者去查阅该章节。编译器是如何发出 E0033 的源码级验证E0033 的实际检查发生在 HIR 类型检查typeck的模式检查路径中。在 rustc_hir_typeck/src/pat.rs 里check_dereferenceable函数负责拦截这类模式fn check_dereferenceable( self, span: Span, expected: Tytcx, inner: Pat_, ) - Result(), ErrorGuaranteed { if let PatKind::Binding(..) inner.kind let Some(pointee_ty) self.shallow_resolve(expected).builtin_deref(true) let ty::Dynamic(..) pointee_ty.kind() { // This is x dyn SomeTrait being reduced from // let x dyn SomeTrait or let box x Boxdyn SomeTrait, an error. let type_str self.ty_to_string(expected); let mut err struct_span_code_err!( self.dcx(), span, E0033, type {} cannot be dereferenced, type_str ); err.span_label(span, format!(type {type_str} cannot be dereferenced)); if self.tcx.sess.teach(err.code.unwrap()) { err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); } return Err(err.emit()); } Ok(()) }从源码可以看出三个精确的触发条件三者同时满足才报错条件含义PatKind::Binding(..) inner.kind解引用模式内部是一个绑定如let x ...中的x即确实要把指向的值绑给变量builtin_deref(true)有解引用结果外层模式是一个内置指针解引用、mut、*或box模式pointee_ty.kind()为ty::Dynamic(..)被解引用出来的“指向类型”是 trait 对象dyn Trait即无编译期尺寸源码注释还特别指出了覆盖范围let x dyn SomeTrait or let box x Boxdyn SomeTrait—— 也就是说不仅dyn TraitBoxdyn Trait上的解引用绑定同样会命中这条路径两者都被归约为“x dyn SomeTrait”这一非法形态。错误消息本身是type \${expected} cannot be dereferenced例如对SomeTrait会报type SomeTrait\cannot be dereferenced具体显示取决于模式上下文中传入的expected类型。而--explain E0033时注入的教学文本CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ是 [pat.rs](https://link.gitcode.com/i/54e4db62d3c8aa53ad3d1ea6d63ea742#L40-L47) 顶部定义的一个常量其措辞与 [E0033.md](https://link.gitcode.com/i/c727673c0f9f966fe4aa60f6562b3f09) 文档正文基本一致——这正是 RFC 1567 要求“文档与诊断保持一致”的体现错误码文档既是给开发者看的说明也是--explain 的输出来源。另外注意报错发生在类型检查阶段而非解析阶段check_dereferenceable需要类型信息expected: Tytcx才能判断指向类型是否为dyn因此这类问题在 typeck 阶段才暴露属于语义错误而非语法错误。正确写法不要绑定直接通过指针使用文档给出的修复原则是“避免解引用该指针改为直接通过指针访问”let trait_obj: SomeTrait some_value; // 方法调用会自动解引用指针不会产生无尺寸的本地变量 trait_obj.method_one(); trait_obj.method_two(); // 也可以继续以引用形式传递 fn consume(p: dyn SomeTrait) { p.method_one(); } consume(trait_obj);常见的正确姿势归纳直接方法调用trait_obj.method()会经过自动解引用deref coercion/autoderef找到 vtable 中的函数指针并调用全程不产生新的值绑定是 trait 对象最常见的消费方式。继续以指针传递把dyn Trait、mut dyn Trait、Boxdyn Trait作为参数或字段类型在程序里流转始终保持“带指针”形态。需要静态分发时改用泛型T: SomeTrait或具体的具体类型concrete type——具体类型有确定尺寸可以安全地解引用绑定。避免let x ptr、let box x box_ptr这类针对 trait 对象的解引用绑定这是 E0033 唯一对应的反模式。小结E0033 的本质是 Rust“无尺寸值不得占据栈上绑定”这一规则在 trait 对象上的具体体现trait 对象是动态大小类型dyn Trait只是“指针 vtable”不含实现者数据let x trait_obj;要求把尺寸未知的值绑定到本地变量编译器无法生成对应代码于是 rustc_hir_typeck/src/pat.rs 中的check_dereferenceable以E0033拒绝该模式修复方式很简单不要绑定被指向的值直接通过指针做方法调用或继续传递指针确需按值绑定时改用泛型或具体类型。遇到E0033时的排查路径也很清晰先看报错行是否出现了针对 trait 对象指针的解引用绑定模式再用rustc --explain E0033或查阅 E0033.md 确认错误语义然后按上述方式重写即可。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表