【问题标题】:Turning a Vec<Rc<RefCell<T>>> into a &[&mut T]将 Vec<Rc<RefCell<T>>> 变成 &[&mut T]
【发布时间】:2016-09-26 11:09:33
【问题描述】:

我有一个引用计数 RefCells 的向量,并希望将 (mut) 引用的 Vec 传递到 RefCells 到函数中。引用不需要超过函数调用。

这似乎应该是可能的(只有一个,像&amp;*x.borrow_mut() 是可以的)。我试图保留RefMut&amp;mut 的中间向量来控制生命周期,但我还没有找到让它工作的方法:

use std::cell::{RefCell,RefMut};
use std::vec::Vec;
use std::rc::Rc;

trait SomeTrait {}

struct Wrapper<'a> {
    pub r: &'a mut SomeTrait,
}

fn foo(_: &[Wrapper]) {}

fn main() {
    let mut v1: Vec<Rc<RefCell<SomeTrait>>> = unimplemented!();

    let mut v_rm: Vec<RefMut<_>> = v1.iter_mut().map(|r| r.borrow_mut()).collect();
    let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
    foo(&v_wrapper[..]);
}

(playground)

显然存在终身问题:

rustc 1.11.0 (9b21dcd6a 2016-08-15)
error: borrowed value does not live long enough
  --> <anon>:17:60
   |>
17 |>     let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
   |>                                                            ^^^^^^^^^^
note: reference must be valid for the block suffix following statement 2 at 17:107...
  --> <anon>:17:108
   |>
17 |>     let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
   |>                                                                                                            ^
note: ...but borrowed value is only valid for the block at 17:71
  --> <anon>:17:72
   |>
17 |>     let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
   |>                                                                        ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

我确实控制 foo,因此可以更改其 API 以使事情变得更容易,但它位于不同的模块/板条箱中,我真的不希望它知道我将我的 SomeTrait 对象保存在 @ 987654333@.

【问题讨论】:

    标签: rust lifetime


    【解决方案1】:

    虽然当然可以编写以Vec&lt;RefMut&lt;T&gt;&gt; 开头的代码并从中创建Vec&lt;&amp;mut T&gt; (generic example),但我建议您更改foo 的签名。许多算法不需要切片提供的随机访问,如果函数可以接受迭代器而不是切片,则您不需要创建 两个 整个额外的Vecs,除了调用函数变得更简单。我在想这样的签名

    fn foo<I, R>(widgets: I)
        where I: IntoIterator<Item=R>,
              R: DerefMut<Target=SomeTrait>
    {
        for widget in widgets {
            // ...
        }
    }
    

    那么您所需要的就是生成一个产生RefMut 的迭代器,这很容易用v1.iter_mut().map(|x| x.borrow_mut()) 完成。 Here's 一个例子。

    【讨论】:

    • 您的wrangle_mut_refs 示例是否真的从Vec&lt;Rc&lt;RefCell&lt;T&gt;&gt;&gt; 开始工作?它与我所拥有的很接近,但似乎仍然无法解决我的代码存在的生命周期问题。
    • 更通用的版本看起来确实是一个改进,只是在这种情况下foo 需要迭代多次,因此依赖IntoIterator 可能不起作用。但是,也许使用DerefMut 可以传入RefMut 的一部分。
    • @ChrisEmerson wrangle_mut_refsVec&lt;RefMut&lt;T&gt;&gt; 工作,但在问题中,您已经从 Vec&lt;Rc&lt;RefCell&lt;T&gt;&gt;&gt; 成功创建了它。
    • 我不确定我的Vec&lt;RefMut&lt;T&gt;&gt; 是否一定可用。 :-( 我尝试使用您的wrangle_mut_refs 但没有成功:playground 生命周期错误现在回到v1 寿命不够长。
    • @ChrisEmerson 有问题的部分是ref mut 模式——不出所料,我可以补充一下,因为这是问题中的代码和wrangle_mut_refs 之间的主要区别;-) 这编译:@987654324 @
    【解决方案2】:

    首先,我同意@delnan 的观点,如果可以的话,您应该切换到基于迭代器的界面。

    大部分代码都很好,在将 fooWrapper 更改为更灵活一点后,我能够调整其余部分并进行编译:

    use std::cell::{RefCell,RefMut};
    use std::vec::Vec;
    use std::rc::Rc;
    
    trait SomeTrait {}
    
    struct Wrapper<'a, 'b> where 'b: 'a {
        pub r: &'a mut (SomeTrait + 'b),
    }
    
    fn foo<'a, 'b>(_: &'a mut [Wrapper<'a, 'b>]) where 'b: 'a {}
    
    fn main() {
        let mut v1: Vec<Rc<RefCell<SomeTrait>>> = unimplemented!();
    
        let mut v_rm: Vec<RefMut<_>> = v1.iter_mut().map(|r| r.borrow_mut()).collect();
        let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|mut rm| Wrapper{ r: &mut **rm }).collect();
        foo(&mut v_wrapper[..]);
    }
    

    这里要理解的关键是 每个 trait 对象类型都有一个隐式的生命周期,因为一个 impl 可能包含引用。没有SomeTrait这样的类型,只有SomeTrait + 'aSomeTrait + 'bSomeTrait + 'static

    您的代码中的问题是 Rust 推断的两件事不匹配。

    • 在你写 Rc&lt;RefCell&lt;SomeTrait&gt;&gt; 的地方,Rust 假设你的意思是 Rc&lt;RefCell&lt;SomeTrait + 'static&gt;&gt;

    • 你在哪里写 fn foo(_: &amp;[Wrapper]) {},应用了不同的规则,Rust 假设你的意思是 fn foo&lt;'a&gt;(_: &amp;'a [Wrapper&lt;'a&gt; + 'a])

    哦。在这些假设下,这个谜题确实没有解决方案,这就是为什么我不得不放松一下。

    如果您不想要 'b 生命周期参数,您可以放弃它,只需在使用它的地方(Wrapper::r 的类型)将 'b 更改为 'static。这不太灵活:您将被限制为具有静态生命周期的 SomeTrait impls。

    【讨论】:

    • 这很有用,谢谢。我想我得到了函数签名中省略生命周期的规则(至少有时如此)。在描述/记录的Rc&lt;RefCell&lt;SomeTrait&gt;&gt; 上推断的'static 在哪里?
    • 我想我找到了:RFC 599
    • ...由RFC 1156修改
    猜你喜欢
    • 2023-01-20
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    相关资源
    最近更新 更多