如何从RefCell< T>借用T.作为参考? [英] How to borrow the T from a RefCell<T> as a reference?

查看:74
本文介绍了如何从RefCell< T>借用T.作为参考?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时我有一个struct,其中包含一个包裹在RefCell中的值,我想借用该值,但是我不想使访问器函数的签名依赖于内部实现.为了使其正常工作,我需要将引用以Ref<T>而不是&T的形式返回.

例如,如果这是我的结构:

use std::cell::RefCell;

pub struct Outer<T> {
    inner: RefCell<T>,
}

我可以这样编写访问器:

use std::cell::Ref;

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> Ref<T> {
        self.inner.borrow()
    }
}

这很好.我可以这样使用它:

fn main() {
    let outer = Outer { inner: RefCell::new(String::from("hi")) };
    let inner: &str = &outer.get_inner_ref();
    println!("inner value = {:?}", inner);
}

但是,这将Ref作为公共API的一部分公开,这将使得以后更难更改内部结构而又不破坏向后兼容性.

如果我尝试更改签名以返回&T(&Ref<T>可以强制转换为该符号),那么我会收到终身错误:

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> &T {
        &self.inner.borrow()
    }
}

错误是:

 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 | |     }
   | |_____^
 

似乎没有办法解决此问题,因为错误消息是正确的.代码尝试引用对Ref<T>的引用,该引用仅持续到函数调用的时间.为了使此工作有效,我必须移回Ref<T>本身,就像上面的原始代码一样,返回它,而不是对其进行新引用.

有一个有关如何在不破坏封装的情况下如何返回对RefCell内部内容的引用的答案?可以解决此问题,但这是一个更特殊的情况(仅获取RefCell中的一部分值),对于这种简单情况,解决方案似乎过于复杂.

解决方案

这正是impl Trait的目的,impl Trait已在稳定的Rust 其他参考之一类型,包括您自己的自定义类型,例如其他链接的问题.

Sometimes I have a struct containing a value which is wrapped in a RefCell, and I want to borrow the value, but I don't want to make the signature of the accessor function to depend on the internal implementation. To make it work, I need to return the reference as a Ref<T> instead of a &T.

For example, if this is my struct:

use std::cell::RefCell;

pub struct Outer<T> {
    inner: RefCell<T>,
}

I could write an accessor like this:

use std::cell::Ref;

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> Ref<T> {
        self.inner.borrow()
    }
}

This works fine. I can use it like this:

fn main() {
    let outer = Outer { inner: RefCell::new(String::from("hi")) };
    let inner: &str = &outer.get_inner_ref();
    println!("inner value = {:?}", inner);
}

However, this exposes Ref as part of the public API, which would make it harder to change the internals later, without breaking backwards compatibility.

If I try change the signature to return an &T — which &Ref<T> can coerce to — then I get lifetime errors:

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> &T {
        &self.inner.borrow()
    }
}

The error is:

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 | |     }
   | |_____^

There doesn't appear to be a way to fix that, because the error message is correct. The code is trying to take a reference to the Ref<T>, which only lasts as long as the function call. To make this work, I'd have to move out the Ref<T> itself by returning it — exactly like in the original code above — rather than making a new reference to it.

There is an answer to How do I return a reference to something inside a RefCell without breaking encapsulation? which would technically solve this, but it is a more specialised case (to get only a part of the value in the RefCell) and the solution seems overly complex for this simpler situation.

解决方案

This is exactly the purpose of impl Trait, which has been available in stable Rust since version 1.26.

use std::ops::Deref;

impl<T> Outer<T> {
    fn get_inner_ref<'a>(&'a self) -> impl Deref<Target = T> + 'a {
        self.inner.borrow()
    }
}

The Rust compiler knows that the actual implementation is Ref<T> but lets you avoid having to write it explicitly, and callers of this function can only use functionality provided by the Deref trait.

As long as the actual value that you return is of a type that implements Deref<Target = T>, you are free to change that implementation later without breaking any code that uses it. For example, you could return &T or one of several other reference types, including your own custom type, as in the other linked question.

这篇关于如何从RefCell&lt; T&gt;借用T.作为参考?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆