不能 return 引用局部变量
Cannot return reference to local variable
我有一个特点:
pub trait HasQuux {
fn quux(&self) -> &Quux;
}
大多数实现此功能的结构都有一个 Quux
字段,可能是嵌套的,它们可以 return 对其进行引用。例如
pub struct Foo {
...
q: Quux,
...
}
impl HasQuux for Foo {
fn quux(&self) -> &Quux {
&self.q
}
}
如何为必须计算其 Quux 的结构实现 HasQuux
?
此实现:
impl HasQuux for Bar {
fn quux(&self) -> &Quux {
let q: Quux = self.calc_quux();
&q
}
}
原因:
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
我热衷于 quux()
到 return 的参考,因为 99% 的实现 HasQuux
的结构都有一个 Quux
字段。
我明白为什么这是不可能的。
我可以创建一个临时的 Quux
,其生命周期与 Bar
的生命周期相匹配,并且 return 是对它的引用吗?
或者有更好的解决方案吗?
Can I create a temporary Quux
whose lifetime matches the lifetime of Bar and return a reference to that?
简短的回答是:你不能。你可以做的是 quux
return 一个 Cow
(写时复制,而不是牛):
fn quux(&self) -> Cow<'_, Quux>
99% 有 self.q
的 impl 会 return Cow::Borrowed(&self.q)
,而 1% 没有 return Cow::Owned(self.calc_quux())
。 (Playground.)
如果您不能更改特征定义,那么您不太可能return 安全 Rust 中的引用(没有 leaking Quux
)。首先,您需要将计算出的 q
存储在 self
内的 Option<Quux>
中,这可能不是您想要的。但即使是这样,您也会遇到 HasQuux::quux
使用 &self
而不是 &mut self
的问题。使用 RefCell<Option<Quux>>
将允许您存储计算出的 Quux
,但不能存储到 return 对它的引用,因为 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()
}
}
}
上述类型在其实现中使用了不安全,但提供了一个完全安全的接口(据我所知 - 不安全代码永远无法 100% 确定)。安全注释中提到的另一个borrow_mut()
不能被外部代码执行,因为Lazy
的字段是私有的。尽管如此,如果 Lazy
的定义被错误修改,编译器无法捕获错误,因为我们使用 unsafe
来说服它我们知道我们在做什么。
有了这个辅助类型,我们可以为 Bar
:
提供一个简单而安全的 HasQuux
实现
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())
}
}
1
或者只使用 OnceCell
type from the once_cell
crate whose get_or_init
方法完全等同于上面定义的 Lazy::ensure()
。
我有一个特点:
pub trait HasQuux {
fn quux(&self) -> &Quux;
}
大多数实现此功能的结构都有一个 Quux
字段,可能是嵌套的,它们可以 return 对其进行引用。例如
pub struct Foo {
...
q: Quux,
...
}
impl HasQuux for Foo {
fn quux(&self) -> &Quux {
&self.q
}
}
如何为必须计算其 Quux 的结构实现 HasQuux
?
此实现:
impl HasQuux for Bar {
fn quux(&self) -> &Quux {
let q: Quux = self.calc_quux();
&q
}
}
原因:
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
我热衷于 quux()
到 return 的参考,因为 99% 的实现 HasQuux
的结构都有一个 Quux
字段。
我明白为什么这是不可能的。
我可以创建一个临时的 Quux
,其生命周期与 Bar
的生命周期相匹配,并且 return 是对它的引用吗?
或者有更好的解决方案吗?
Can I create a temporary
Quux
whose lifetime matches the lifetime of Bar and return a reference to that?
简短的回答是:你不能。你可以做的是 quux
return 一个 Cow
(写时复制,而不是牛):
fn quux(&self) -> Cow<'_, Quux>
99% 有 self.q
的 impl 会 return Cow::Borrowed(&self.q)
,而 1% 没有 return Cow::Owned(self.calc_quux())
。 (Playground.)
如果您不能更改特征定义,那么您不太可能return 安全 Rust 中的引用(没有 leaking Quux
)。首先,您需要将计算出的 q
存储在 self
内的 Option<Quux>
中,这可能不是您想要的。但即使是这样,您也会遇到 HasQuux::quux
使用 &self
而不是 &mut self
的问题。使用 RefCell<Option<Quux>>
将允许您存储计算出的 Quux
,但不能存储到 return 对它的引用,因为 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()
}
}
}
上述类型在其实现中使用了不安全,但提供了一个完全安全的接口(据我所知 - 不安全代码永远无法 100% 确定)。安全注释中提到的另一个borrow_mut()
不能被外部代码执行,因为Lazy
的字段是私有的。尽管如此,如果 Lazy
的定义被错误修改,编译器无法捕获错误,因为我们使用 unsafe
来说服它我们知道我们在做什么。
有了这个辅助类型,我们可以为 Bar
:
HasQuux
实现
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())
}
}
1
或者只使用 OnceCell
type from the once_cell
crate whose get_or_init
方法完全等同于上面定义的 Lazy::ensure()
。