Rust:如何移动映射&mut T后的原值并将其替换为新T?
Rust中替换&mut T指向实例的方案及
map_in_place类API的相关问题 你想要将&mut T指向的T实例,替换为用旧实例构造出的新实例,原来的写法编译失败,是因为Rust不允许从可变引用后直接移出值——如果构造新值的过程中发生panic,旧值已经被部分移出,&mut T就会指向非法内存,违反内存安全规则。
你使用std::mem::replace的现有方案是正确的,逻辑是先向&mut T指向的位置写入一个合法的占位实例,把旧实例完整取出来,再用旧实例构造新值写回,哪怕构造新值时panic,占位实例也是合法的,不会出现内存悬空。如果T实现了Default,还可以用更简洁的std::mem::take,它会自动生成默认占位值:
let old_t = std::mem::take(t); *t = T { s: old_t.s + " world" };
关于你提到的map_in_place类API没有成为标准库通用API的原因,主要有三点:
- 安全约束限制通用性:如果要在无占位值的前提下实现这个API,闭包执行panic时会导致
&mut T指向未初始化内存,触发未定义行为。如果要求T必须可以构造占位值(比如实现Default),又会大幅缩小API的适用范围,标准库没有必要为这种有前提的场景做单独封装。 - 已有基础组件可以快速实现:你完全可以基于
std::mem::take自行封装符合需求的map_in_place,代码非常简单:
封装之后你就可以直接调用fn map_in_place<T: Default>(t: &mut T, f: impl FnOnce(T) -> T) { let old = std::mem::take(t); *t = f(old); }map_in_place(t, |old_t| T { s: old_t.s + " world" }),完全符合你的预期。 - 无占位的不安全实现不符合标准库设计原则:如果要绕过占位值的要求,需要用到unsafe代码,且panic时只能通过终止进程避免未定义行为,这种取舍不适合放进标准库,你可以通过第三方库实现这类需求。
内容的提问来源于stack exchange,提问作者William Stanley
相关产品推荐
相关产品推荐

