Rust中的uwrap_or_else如何返回一个引用

3

我在一段动态规划中遇到了一种情况,要么获取预先计算的结果,要么调用函数计算这些结果。

这就是简短的情况说明。

let previous_results HashMap<String, Result> = HashMap::new();

for i in some_values {
   let result = previous_results.get(i).unwrap_or_else(|| calculate_results(i))
}

Rust编译器在函数调用时发出了合理的警告,并指出:

expected reference `&HashMap<std::string::String, Result>`
      found struct `HashMap<std::string::String, Result>`

这是因为.get通常返回一个对象的引用,而不是实际的对象,但该函数返回实际的对象。所以我可以返回该函数返回的内容的引用。
let result = previous_results.get(i).unwrap_or_else(|| &calculate_results(i))

请注意函数调用前面的&。但这也是一个问题,因为匿名函数范围内的引用在匿名函数返回后将变得无意义,这会导致 Rust 抱怨。
cannot return reference to temporary value
returns a reference to data owned by the current function

我在这里缺少什么?使用 Rust 正确的方法是什么?

2个回答

4

你不能返回对局部变量的引用(在这种情况下是unwrap_or_else回调函数),因为当该局部变量被丢弃时,引用将失效。

你可以克隆从映射中取出的值,但这通常不是最有效的方法。而且也许该值甚至不可克隆。

因此,这就是使用Cow的一个用例:

use std::borrow::Cow;

for i in some_values {
   let result = previous_results
       .get(i)
       .map(Cow::Borrowed)
       .unwrap_or_else(|| Cow::Owned(calculate_results(i)));
}

然后你就可以像平常一样使用result,因为它实现了Deref<Result>。但是请记住里面的值可能是从映射表中借用的,所以Cow<'_, Result>值将继续借用映射表。

有趣的是,尽管名称Cow代表Clone-On-Write(写时复制),但很多时候它被用于需要Owned_Or_Borrowed(拥有或借用)的情况。

Cow的主要缺点是它要求该值必须是ToOwned,这基本上意味着要实现Clone,因此名称中包含了C。如果你的Result类型不是Clone,我认为目前在std中没有解决方案,但很容易实现自己的解决方案,或者使用可用的crate,如maybe-owned


2

@rodrigo已经用正确的方法回答了我的问题,但是我有点困惑Cow的神奇之处在于它是如何解决我的问题的,因此我想为其他可能也感到困惑的人澄清一下。

对于我的问题,关键问题在于.get返回HashMap中元素的引用,但由unwrap_or_else调用的函数需要返回一个拥有值,否则一旦我们超出匿名函数作用域,它就不再有效。

因此,在@rodrigo的答案中隐藏着的解决方案是在get之后调用的map,可以用它将get返回的任何内容转换为拥有值,以便unwrap_or_else可以返回一个拥有值,从而解决问题。

Cow的帮助在于克隆.get返回的值并不是最有效的做法,除非你确实需要稍后进行复制。所以Cow抽象了所有权,并使.getunwrap_or_else返回相同的类型,而无需克隆任何内容。

@rodrigo的答案仍然应该是被接受的答案,因为它正确地解决了问题,但我想提供一些更多的背景信息,因为我不太确定所有权是如何解决的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接