能否在安全Rust中实现枚举变体转换(无需unsafe代码)?
如何用安全Rust实现枚举变体的转换?
我有一个接收包含两个变体的枚举的函数,需要将其转换为特定变体。目前只能通过unsafe的ptr::{read,write}让代码编译,但我认为应该可以用安全代码实现——因为是无条件赋值给*self,所以消耗当前值的部分内容应该是可行的。请问是否有办法用安全Rust编写这个函数?
编译错误信息
error[E0507]: cannot move out of `*x` which is behind a mutable reference --> src/lib.rs:9:29 | 9 | *self = Self::B(*x); | ^^ move occurs because `*x` has type `T`, which does not implement the `Copy` trait
代码示例
enum Foo<T> { A(T), B(T), } impl<T> Foo<T> { fn ensure_b(&mut self) { if let Self::A(x) = self { *self = Self::B(*x); } } unsafe fn ensure_b_unsafe(&mut self) { if let Self::A(x) = self { let new_self = Self::B(core::ptr::read(x)); core::ptr::write(self, new_self); } } }
安全实现方案
可以通过std::mem::replace结合MaybeUninit实现完全安全的版本,无需依赖T实现任何额外 trait:
use std::mem::{self, MaybeUninit}; enum Foo<T> { A(T), B(T), } impl<T> Foo<T> { fn ensure_b(&mut self) { // 创建临时占位的Foo::B,内部T未初始化(后续会被立即覆盖) let placeholder = Foo::B(unsafe { MaybeUninit::uninit().assume_init() }); // 取出当前self的旧值,同时将占位符写入self let old = mem::replace(self, placeholder); match old { Foo::A(x) => *self = Foo::B(x), Foo::B(x) => *self = Foo::B(x), } } }
思路说明
mem::replace会先取出self指向的完整枚举值,同时将占位符写入self,这样就能安全拥有旧值的所有权,避免了“可变引用后移值”的编译错误。- 占位符里的未初始化
T不会被使用,因为紧接着就会把新的Foo::B变体写入self覆盖占位符,完全符合Rust安全规则。
如果你的T实现了Default,还可以用更简洁的std::mem::take:
use std::mem; enum Foo<T> { A(T), B(T), } impl<T: Default> Default for Foo<T> { fn default() -> Self { Foo::A(T::default()) } } impl<T: Default> Foo<T> { fn ensure_b(&mut self) { match mem::take(self) { Foo::A(x) => *self = Foo::B(x), Foo::B(x) => *self = Foo::B(x), } } }
mem::take会自动用Default值替换self,无需手动创建占位符,但缺点是依赖T实现Default trait。
内容的提问来源于stack exchange,提问作者ajp
相关产品推荐
相关产品推荐

