Pet*_*all 2 generics reference rust borrow-checker
有时我有一个struct包含在a 中的值RefCell,我想借用该值,但我不想让访问器函数的签名依赖于内部实现.为了使它工作,我需要返回引用Ref<T>而不是a &T.
例如,如果这是我的结构:
use std::cell::RefCell;
pub struct Outer<T> {
inner: RefCell<T>,
}
Run Code Online (Sandbox Code Playgroud)
我可以写一个这样的访问器:
use std::cell::Ref;
impl<T> Outer<T> {
fn get_inner_ref(&self) -> Ref<T> {
self.inner.borrow()
}
}
Run Code Online (Sandbox Code Playgroud)
这很好用.我可以像这样使用它:
fn main() {
let outer = Outer { inner: RefCell::new(String::from("hi")) };
let inner: &str = &outer.get_inner_ref();
println!("inner value = {:?}", inner);
}
Run Code Online (Sandbox Code Playgroud)
但是,这会暴露Ref为公共API的一部分,这将使以后更改内部更加困难,而不会破坏向后兼容性.
如果我尝试更改签名以返回&T- &Ref<T>可以强制执行 - 那么我会遇到终生错误:
impl<T> Outer<T> {
fn get_inner_ref(&self) -> &T {
&self.inner.borrow()
}
}
Run Code Online (Sandbox Code Playgroud)
错误是:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:16:10
|
16 | &self.inner.borrow()
| ^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
17 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 15:5...
--> src/main.rs:15:5
|
15 | / fn get_inner_ref(&self) -> &T {
16 | | &self.inner.borrow()
17 | | }
| |_____^
Run Code Online (Sandbox Code Playgroud)
似乎没有办法解决这个问题,因为错误消息是正确的.代码试图引用Ref<T>它,只持续函数调用.为了使这个工作,我必须Ref<T>通过返回它来移出它 - 就像在上面的原始代码中一样 - 而不是对它进行新的引用.
有一个答案如何在不破坏封装的情况下返回对RefCell内部内容的引用?这在技术上可以解决这个问题,但它是一个更专业的案例(只获取部分价值RefCell),对于这种简单的情况,解决方案似乎过于复杂.
这正是自1.26版以来impl Trait在稳定的Rust中可用的目的.
use std::ops::Deref;
impl<T> Outer<T> {
fn get_inner_ref<'a>(&'a self) -> impl Deref<Target = T> + 'a {
self.inner.borrow()
}
}
Run Code Online (Sandbox Code Playgroud)
Rust编译器知道实际的实现是Ref<T>但是允许您避免必须显式地编写它,并且此函数的调用者只能使用Deref特征提供的功能.
只要您返回的实际值是实现的类型Deref<Target = T>,您就可以在以后更改该实现而不会破坏任何使用它的代码.例如,您可以返回&T或其他几种引用类型之一,包括您自己的自定义类型,如在另一个链接的问题中.