及解决方法)
Rust 编译器错误 E0161 详解为什么不能移动未知大小的值dyn 类型及解决方法【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustE0161cannot move a value of type ...是 Rust 编译器在类型检查阶段报出的经典错误触发原因是你试图移动move一个在编译期无法确定大小unsized的值例如裸的dyn Trait对象。读完本文你将理解该错误背后的Sized规则与 MIR 层面的检查机制掌握通过引用、Box、Rc等方式绕过限制的完整方案并能定位到 rustc 源码中产生该诊断的具体位置。E0161 错误是什么E0161 的完整诊断信息形如error[E0161]: cannot move a value of type dyn Bar -- src/main.rs:6:5 | LL | b.f(); | ^ the size of dyn Bar cannot be statically determined官方错误文档对该错误的定义是尝试移动一个在编译期大小未知的值a value was moved whose size was not known at compile time。在 Rust 中只有当类型在编译期大小已知即实现了Sized时值才能被直接移动、赋值或按值传递dyn Trait、切片[T]、C 风格字符串str这类动态大小类型不满足该前提。触发错误的典型代码官方错误文档 E0161.md 给出的原始示例是trait Bar { fn f(self); // 按值消费 self } impl Bar for i32 { fn f(self) {} } fn main() { let b: Boxdyn Bar Box::new(0i32); b.f(); // error[E0161]: cannot move a value of type dyn Bar: // the size of dyn Bar cannot be statically determined }这里的关键在于方法f的接收者是self按值移动。当你调用b.f()时编译器需要将b解引用后按值移动dyn Bar这个本体。虽然Boxdyn Bar本身是定长的一个指针但被移动的值类型是dyn Bar而非Boxdyn Bar——dyn Bar的实际数据大小在编译期不可知因此按值移动它无法生成合法代码编译器直接报 E0161。仓库中的 UI 测试 E0161.rs 验证了同一场景并特意注释说明这是为了确认 E0161 在任何可能影响它的配置下都是硬错误// Check that E0161 is a hard error in all possible configurations that might // affect it. #![crate_type lib] trait Bar { fn f(self); } fn foo(x: Boxdyn Bar) { x.f(); //~^ ERROR E0161 }对应的期望诊断输出见 E0161.stderrerror[E0161]: cannot move a value of type dyn Bar -- $DIR/E0161.rs:11:5 | LL | x.f(); | ^ the size of dyn Bar cannot be statically determined注意错误 span 指向的是调用语句x.f();整体而不是dyn类型出现的位置——这一点与源码中的检查点位置一致下文会说明。官方推荐的修复方案把值藏在引用后面错误文档给出的解决方法是将值隐藏在引用后面——使用x或mut x。引用的大小是固定的普通指针 8 字节、胖指针 16 字节因此可以像普通值一样自由移动和传递。具体做法是把 trait 方法的接收者从self改为selftrait Bar { fn f(self); // 改为借用 self } impl Bar for i32 { fn f(self) {} } fn main() { let b: Boxdyn Bar Box::new(0i32); b.f(); // ok! }此时b.f()实际执行的是Bar::f(*b)先解引用得到dyn Bar的借用再传入定长的引用全程没有移动dyn Bar本体编译通过。如果方法确实需要消费对象比如要取出内部所有权正确姿势是移动指针而不是移动本体trait Bar { fn f(self); // 保持按值语义 } impl Bar for i32 { fn f(self) {} } fn main() { let b: Boxdyn Bar Box::new(0i32); Box::into_inner(b).f(); // 移动 Box定长指针而不是 dyn Bar // 或者把参数类型声明为 Boxdyn Bar由调用方负责解包 }同理函数参数、返回值中也不能直接出现裸的dyn Trait这通常是另一个更早期的unsized type相关报错应写成dyn Trait、Boxdyn Trait、Rcdyn Trait等定长包装形式。源码级解析E0161 在哪里、如何产生E0161 的诊断结构体定义在 session_diagnostics.rs#[derive(Diagnostic)] #[diag(cannot move a value of type {$ty}, code E0161)] pub(crate) struct MoveUnsizedtcx { pub ty: Tytcx, #[primary_span] #[label(the size of {$ty} cannot be statically determined)] pub span: Span, }两个diag/label字符串正好对应你在终端看到的主错误行 ^下划线处的补充说明与 E0161.stderr 中的输出逐字一致。真正触发该诊断的是 MIR 类型检查中的ensure_place_sized方法位于 type_check/mod.rsfn ensure_place_sized(mut self, ty: Tytcx, span: Span) { let tcx self.tcx(); // Erase the regions from ty to get a global type. ... let erased_ty tcx.erase_and_anonymize_regions(ty); // FIXME(#132279): Using Ty::is_sized causes us to incorrectly handle opaques here. if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) { // in current MIR construction, all non-control-flow rvalue // expressions evaluate through as_temp or into a return // slot or local, so to find all unsized rvalues it is enough // to check all temps, return slots and locals. if self.reported_errors.replace((ty, span)).is_none() { // While this is located in nll::typeck this error is not // an NLL error, its a required check to prevent creation // of unsized rvalues in a call expression. self.tcx().dcx().emit_err(MoveUnsized { ty, span }); } } }从源码结构看检查逻辑包含三个要点判据是Sized而非字面大小先把类型中的区域lifetime擦除得到全局类型Sized判定与精确的 lifetime 无关然后调用is_sized判断。只要类型不满足Sized且该类型出现在一个值被求值/存放的位置临时变量、返回值槽、局部变量就会报 E0161。检查点覆盖所有临时对象temps同文件上方 type_check/mod.rs 中遍历LocalKind::Temp声明时逐一调用ensure_place_sized。源码注释解释了为什么这样做就够当前 MIR 构造中所有非控制流的 rvalue 表达式都会经过as_temp或求值进返回槽/局部变量所以只要检查所有 temps、返回槽和局部变量就能覆盖全部 unsized rvalue——b.f()这种按值调用生成的临时求值对象正落入此列。同一位置只报一次reported_errors.replace(...)的去重机制保证同一处 unsized 类型不会重复刷屏且注释明确指出该检查虽然位于 borrowckNLL 类型检查中但本质上是防止在调用表达式中创建 unsized rvalue的必备检查不属于借用检查规则本身。这解释了测试输出中 span 指向整条调用语句的现象错误 span 来自生成该临时对象的 MIR 位置信息local_decl.source_info.span而非类型标注处。适用前提与限制本错误是硬错误不依赖任何 feature gateE0161.rs 的注释表明团队专门验证了它在各种可能相关的配置下如不同crate_type始终成立。源码中ensure_place_sized存在一个已知的 FIXME引用问题 #132279当前实现对 opaque 类型如impl Trait背后的类型的is_sized处理尚不完美可以推断这类边缘情况下的诊断行为可能随编译器版本演进。当 unstable featureunsized_fn_params启用时对参数位置的检查会转移到终止符terminator路径上见 type_check/mod.rs 中if self.tcx().features().unsized_fn_params()分支但在稳定版行为不受影响。总结项目内容错误码E0161触发条件尝试移动/求值一个不满足Sized的值如裸dyn Trait、[T]、str诊断来源MoveUnsized由 ensure_place_sized 在 MIR 类型检查中发出首选修复将self方法改为self或mut self通过引用传递需要消费时传递/解包定长智能指针如Boxdyn Trait、Rcdyn Trait而不是裸dyn Trait参考测试tests/ui/error-codes/E0161.rs、E0161.stderr核心记忆点一句话Rust 只允许移动编译期大小已知的值遇到 E0161 时把移动本体改为移动指向本体的定长指针引用或智能指针即可。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考