fad*_*bee 1 reference rust borrow-checker
我有一个特点:
pub trait HasQuux {
fn quux(&self) -> &Quux;
}
Run Code Online (Sandbox Code Playgroud)
大多数实现此功能的结构都有一个Quux可能是嵌套的字段,它们可以返回一个引用。例如
pub struct Foo {
...
q: Quux,
...
}
impl HasQuux for Foo {
fn quux(&self) -> &Quux {
&self.q
}
}
Run Code Online (Sandbox Code Playgroud)
我如何实现HasQuux必须计算 Quux 的结构?
这个实现:
impl HasQuux for Bar {
fn quux(&self) -> &Quux {
let q: Quux = self.calc_quux();
&q
}
}
Run Code Online (Sandbox Code Playgroud)
原因:
error[E0515]: cannot return reference to local variable `q`
--> /home/fadedbee/test.rs:38:3
|
38 | &q
| ^^ returns a reference to data owned by the current function
Run Code Online (Sandbox Code Playgroud)
我渴望quux()返回一个引用,因为 99% 的结构实现HasQuux都有一个Quux字段。
我明白为什么这是不可能的。
我可以创建一个Quux其生命周期与其生命周期相匹配的临时对象Bar并返回对其的引用吗?
或者有更好的解决方案吗?
我可以创建一个
Quux其生命周期与 Bar 的生命周期匹配的临时对象并返回对其的引用吗?
简短的回答是:你不能。你能做的就是quux返回一个Cow(写时复制,而不是牛):
fn quux(&self) -> Cow<'_, Quux>
Run Code Online (Sandbox Code Playgroud)
99% 具有 a 的 implsself.q将会返回Cow::Borrowed(&self.q),而 1% 没有的 impls 将会返回Cow::Owned(self.calc_quux())。(游乐场。)
如果您无法更改特征定义,那么您不太可能成功返回安全 Rust 中的引用(不会泄漏 Quux)。首先,您需要将计算结果存储q在Option<Quux>inside中self,这可能不是您想要的。HasQuux::quux但即使是这样,你也会遇到接受&self和不接受的问题&mut self。使用RefCell<Option<Quux>>将允许您存储计算的Quux,但不能返回对其的引用,因为 Rust 无法阻止您覆盖RefCell代码中其他位置的内容。
如果可以接受缓存&Quux给定的内容,则可以实现帮助器类型1来封装内部可变性:
mod lazy {
use std::cell::RefCell;
#[derive(Debug, Default)]
pub struct Lazy<T> {
content: RefCell<Option<T>>,
}
impl<T> Lazy<T> {
pub fn ensure(&self, make_content: impl FnOnce() -> T) -> &T {
if self.content.borrow().is_none() {
*self.content.borrow_mut() = Some(make_content());
}
// safety: self.content.borrow_mut() must never be called again
// because we give out a shared reference to the content
let content = unsafe { &*self.content.as_ptr() };
// unwrap: we've ensured above that the option is Some
content.as_ref().unwrap()
}
}
}
Run Code Online (Sandbox Code Playgroud)
上述类型在其实现中使用了 unsafe,但提供了一个完全安全的接口(据我所知 - 对于不安全的代码,人们永远无法 100% 确定)。安全注释中提到的另一个borrow_mut()不能被外部代码执行,因为 的字段Lazy是私有的。尽管如此,如果 的定义Lazy被错误地修改,编译器将无法捕获错误,因为我们过去常常unsafe说服它我们知道我们在做什么。
HasQuux通过这种辅助类型,我们可以提供for的简单且安全的实现Bar:
struct Bar {
cached_q: lazy::Lazy<Quux>,
}
impl Bar {
fn calc_quux(&self) -> Quux {
Quux
}
}
impl HasQuux for Bar {
fn quux(&self) -> &Quux {
self.cached_q.ensure(|| self.calc_quux())
}
}
Run Code Online (Sandbox Code Playgroud)
或者只使用板条箱OnceCell中的类型once_cell,其get_or_init方法与上面定义的方法完全相同Lazy::ensure()。