TLDR:我想将&mut T
后面的T
替换为从旧的T
构建的新T
注意:如果这个问题的解决方案很容易找到,请原谅我。我做了很多谷歌搜索,但我不确定如何正确地表达这个问题。
示例代码(playground):
struct T { s: String }
fn main() {
let ref mut t = T { s: "hello".to_string() };
*t = T {
s: t.s + " world"
}
}
这显然会失败,因为String
上的add impl按值获取self
,因此将需要能够移出T
,然而这是不可能的,因为T
在引用之后。
根据我所能找到的,实现这一点的通常方法是这样做的
let old_t = std::mem::replace(t, T { s: Default::default() });
t.s = old_t + " world";
但这要求创建一些占位符T
是可能的和可行的,直到我们可以用真实的数据填充它。
幸运的是,在我的用例中,我可以创建一个占位符T
,但我仍然不清楚为什么不可能有类似的api:
map_in_place(t, |old_t: T| T { s: old_t.s + " world" });
是否有不可能或通常不做的原因?
1条答案
按热度按时间dbf7pr2w1#
[
map_in_place
]不可行或通常无法完成的原因是什么?一个
map_in_place
确实是可能的:但不幸的是,它并不可靠。如果
f()
出现问题,*place
将被丢弃两次。第一次,它将在展开f()
的作用域时被丢弃,因为f()
认为它拥有它所接收到的值。然后,它将被place
所借用的值的所有者第二次丢弃。它从来没有注意到它认为它拥有的值实际上是垃圾,因为它已经被删除了。这甚至可以在in the playground中重现,其中闭包中的一个简单panic!()
会导致双free。由于这个原因,
map_in_place
的实现本身必须被标记为不安全的,并且有一个安全契约,f()
不会发生恐慌。但是,由于Rust中几乎任何东西都可能发生恐慌(例如,任何切片访问),因此很难确保安全契约和函数会有点像一把猎枪。replace_with
crate确实提供了这样的功能,在出现恐慌的情况下有几个恢复选项。从文档来看,作者敏锐地意识到了恐慌问题,所以如果你真的需要那个功能,那可能是一个很好的地方。