我的程序被构造为一系列函数调用,以建立结果值-每个函数将返回的值返回(移动)到其调用方。这是一个简化的版本:
struct Value {}
struct ValueBuilder {}
impl ValueBuilder {
pub fn do_things_with_value(&mut self, v : &Value) {
// expensive computations
}
pub fn make_value(&self) -> Value {
Value {}
}
pub fn f(&mut self) -> Value {
let v = self.make_value();
self.do_things_with_value(&v);
v
}
pub fn g(&mut self) -> Value {
let v = self.f();
self.do_things_with_value(&v);
v
}
}
想象一下,在它们之间以及上面,还有更多类似于f和g的函数。您可以看到do_things_with_value
被两次调用且具有相同的值。我想缓存/存储此调用,以便在下面的示例中,“昂贵的计算”仅执行一次。这是我的尝试(显然不正确):
#[derive(PartialEq)]
struct Value {}
struct ValueBuilder<'a> {
seen_values: Vec<&'a Value>,
}
impl<'a> ValueBuilder<'a> {
pub fn do_things_with_value(&mut self, v: &'a Value) {
if self.seen_values.iter().any(|x| **x == *v) {
return;
}
self.seen_values.push(v)
// expensive computations
}
pub fn make_value(&self) -> Value {
Value {}
}
pub fn f(&mut self) -> Value {
let v = self.make_value();
self.do_things_with_value(&v); // error: `v` does not live long enough
v
}
pub fn g(&mut self) -> Value {
let v = self.f();
self.do_things_with_value(&v);
v
}
}
我理解编译器为什么这样做-在这种情况下,碰巧在两次调用do_things_with_value
之间没有删除v,但不能保证不会删除它,而取消引用它会导致崩溃程序。
构造此程序的更好方法是什么?假设:
Values
很昂贵,我们负担不起seen_values
保留我们见过的所有内容的副本Value
对象以携带其他数据(例如,表示是否使用此值进行了昂贵的计算的布尔值)。它需要依靠使用PartialEq
答案 0 :(得分:3)
如果您需要在程序的不同位置保留相同的值,则最容易复制或克隆它。
但是,如果由于克隆成本太高而无法克隆,则将值包装在Rc
中。那是一个引用计数的智能指针,它允许对其内容进行共享所有权。克隆而不复制所包含的值相对便宜。
请注意,仅将Rc<Value>
存储在seen_values
中将至少在价值构建者有效期内保持所有价值不变。您可以通过存储Weak
个引用来避免这种情况。
use std::rc::{Rc, Weak};
#[derive(PartialEq)]
struct Value {}
struct ValueBuilder {
seen_values: Vec<Weak<Value>>,
}
impl ValueBuilder {
pub fn do_things_with_value(&mut self, v: &Rc<Value>) {
if self
.seen_values
.iter()
.any(|x| x.upgrade().as_ref() == Some(v))
{
return;
}
self.seen_values.push(Rc::downgrade(v))
// expensive computations
}
pub fn make_value(&self) -> Rc<Value> {
Rc::new(Value {})
}
pub fn f(&mut self) -> Rc<Value> {
let v = self.make_value();
self.do_things_with_value(&v);
v
}
pub fn g(&mut self) -> Rc<Value> {
let v = self.f();
self.do_things_with_value(&v);
v
}
}
函数Rc<Value>
的链中正在使用do_things()
时,将记住该值并跳过计算。如果某个值变为未使用的值(所有引用均被删除)并随后再次创建,则do_things()
将重复计算。