我有一个结构,该结构拥有某种特征类型的装箱值。该结构本身也实现了相同的特征。我想用相同结构的新实例替换该值,然后将其包装。
以下未编译的代码应使我更清楚地知道要做什么:
trait T {}
struct S {
t: Box<dyn T>,
}
impl T for S {}
impl S {
fn new(t: Box<dyn T>) -> Self {
Self { t }
}
fn wrap_t(&mut self) {
self.t = Box::new(Self::new(self.t))
}
}
此操作失败:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:14:37
|
14 | self.t = Box::new(Self::new(self.t))
| ^^^^ cannot move out of borrowed content
像这样实现wrap_t
会编译:
use std::mem;
fn wrap_t(&mut self) {
unsafe {
let old_t = mem::replace(&mut self.t, mem::uninitialized());
let new_t = Box::new(Self::new(old_t));
let uninit = mem::replace(&mut self.t, new_t);
mem::forget(uninit);
}
}
我想知道是否存在一种安全的方法。
答案 0 :(得分:1)
您正在使用的唯一next
函数是unsafe
。您需要传递给mem::uninitialized
的内容,但是实现mem::replace
无效,因为Default
返回default()
,这会导致对象安全。同样,您无法实现Self
来复制旧值,因为Clone
还会返回clone()
。
不过,您可以为此目的实现一个虚拟类型:
Self
您现在也不需要在这里使用struct Dummy;
impl T for Dummy {}
fn wrap_t(&mut self) {
let old_t = mem::replace(&mut self.t, Box::new(Dummy));
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
(我假设这样做是为了防止未初始化内存被删除时的未定义行为)。
作为mem::forget
的替代方法,您可以自己滚动,克隆到Clone
,避免方法签名中包含Box<dyn T>
,因此特征保持对象安全:
Self
trait T: Debug {
fn clone_in_box(&self) -> Box<dyn T>;
}
impl T for S {
fn clone_in_box(&self) -> Box<dyn T> {
Box::new(S {
t: self.t.clone_in_box(),
})
}
}
还有另一种设计,当阅读代码时,它更容易理解。那只是消耗fn wrap_t(&mut self) {
let cloned = self.clone_in_box();
let old_t = mem::replace(&mut self.t, cloned);
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
并返回一个新对象:
self
而不是这个:
fn wrap_t(self) -> Self {
Self::new(Box::new(Self::new(self.t)))
}
您会这样做:
s.wrap_t();