我如何 return 对可选结构字段内值的引用?

How do I return a reference to the value inside an optional struct field?

我正在尝试改进 The Rust Programming Language 中描述的 Cacher 类型。其中一项改进建议指出 Cacher 应该可用于多种类型。为此,我编写了以下代码:

struct Cacher<T, U>
where
    T: Fn(&U) -> U,
{
    calculation: T,
    value: Option<U>,
}

impl<T, U> Cacher<T, U>
where
    T: Fn(&U) -> U,
{
    fn new(calculation: T) -> Cacher<T, U> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: &U) -> &U {
        match self.value {
            Some(v) => &v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                &v
            }
        }
    }
}

编译器抱怨:

error[E0507]: cannot move out of `self.value.0` which is behind a mutable reference
  --> src/lib.rs:21:15
   |
21 |         match self.value {
   |               ^^^^^^^^^^ help: consider borrowing here: `&self.value`
22 |             Some(v) => &v,
   |                  -
   |                  |
   |                  data moved here
   |                  move occurs because `v` has type `U`, which does not implement the `Copy` trait

error[E0515]: cannot return reference to local variable `v`
  --> src/lib.rs:22:24
   |
22 |             Some(v) => &v,
   |                        ^^ returns a reference to data owned by the current function

error[E0515]: cannot return reference to local variable `v`
  --> src/lib.rs:26:17
   |
26 |                 &v
   |                 ^^ returns a reference to data owned by the current function

error[E0382]: borrow of moved value: `v`
  --> src/lib.rs:26:17
   |
24 |                 let v = (self.calculation)(arg);
   |                     - move occurs because `v` has type `U`, which does not implement the `Copy` trait
25 |                 self.value = Some(v);
   |                                   - value moved here
26 |                 &v
   |                 ^^ value borrowed here after move

我想这是因为 v 是函数的局部变量。但是,鉴于实际数据存在于 value 方法之外的结构中,是否不可能以任何方式 return 引用此数据?如果不是这样,那么我需要做些什么才能获得拥有计算数据的 Cacher 的功能,并在需要时 returns 引用?

你差不多明白了。你只需要 return 对象的引用 inside Option:

struct Cacher<T, U>
where
    T: Fn(&U) -> U
{
    calculation: T,
    value: Option<U>,
}

impl<T, U> Cacher<T, U>
where
    T: Fn(&U) -> U
{
    fn new(calculation: T) -> Cacher<T, U> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: &U) -> &U {
        if self.value.is_none() {
            let v = (self.calculation)(arg);
            self.value = Some(v);
        }
        self.value.as_ref().unwrap()
    }
}

Playground

一个更简单且稍微更有效的替代方法是使用 get_or_insert_with:

fn value(&mut self, arg: &U) -> &U {
    let calculation = &self.calculation;
    self.value.get_or_insert_with(|| calculation(arg))
}