首页 > 解决方案 > 将内部引用传递给泛型闭包时的关联类型生存期

问题描述

这是一个编译良好的示例:

use std::cell::RefCell;
use std::rc::Rc;

struct Foo<'a> {
    val: Rc<RefCell<i32>>,
    dummy: Option<&'a i32>,
}

fn consume<T>(_: T) {}

impl<'a> Foo<'a> {
    // Note that &i32 has no lifetime markers
    fn subscribe<F>(self, func: F)
    where
        F: Fn(&i32) + 'a,
    {
        let val = self.val.clone();
        consume(move |x: i32| {
            *val.borrow_mut() = x;
            func(&*val.borrow())
        })
    }
}

这是我想要实现但未编译的内容:

use std::cell::RefCell;
use std::rc::Rc;

trait Stream<'a> {
    type Item: 'a;

    fn subscribe<F>(self, func: F)
    where
        F: Fn(Self::Item) + 'a;
}

struct Bar<'a, S: Stream<'a>> {
    stream: S,
    val: Rc<RefCell<S::Item>>,
}

impl<'a, S: Stream<'a>> Stream<'a> for Bar<'a, S> {
    type Item = &'a S::Item; // 'a doesn't seem right here...

    fn subscribe<F>(self, func: F)
    where
        F: Fn(Self::Item) + 'a,
    {
        let val = self.val.clone();
        self.stream.subscribe(move |x: S::Item| {
            *val.borrow_mut() = x;
            func(&*val.borrow());
        })
    }
}

这个例子几乎与第一个相同。唯一的区别是,因为它是一个特征,我们必须为关联类型分配一个显式的生命周期Item,它是一个引用。将其设置为'a会导致生命周期冲突(理所当然):

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:27:24
   |
27 |             func(&*val.borrow());
   |                        ^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime  as defined on the body at 25:31...
  --> src/lib.rs:25:31
   |
25 |         self.stream.subscribe(move |x: S::Item| {
   |                               ^^^^^^^^^^^^^^^^^
note: ...so that closure can access `val`
  --> src/lib.rs:27:20
   |
27 |             func(&*val.borrow());
   |                    ^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 17:6...
  --> src/lib.rs:17:6
   |
17 | impl<'a, S: Stream<'a>> Stream<'a> for Bar<'a, S> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:27:18
   |
27 |             func(&*val.borrow());
   |                  ^^^^^^^^^^^^^^

实际上,如果您要在函数签名中替换Fn(&i32)为,则可以将第一个示例修改为失败并返回完全相同的错误。Fn(&'a i32)

是否可以编译第二个示例?可能通过使用一些黑客或不安全的块,我愿意接受任何真正的东西。如果需要,更改签名或重新调整逻辑。关联类型的生命周期应该Item是多少?

标签: rust

解决方案


您可以通过指定F必须能够处理更通用的生命周期来编译第一个示例:

impl<'a> Foo<'a> {
    fn subscribe<F>(self, func: F)
    where
        for<'b> F: Fn(&'b i32) + 'a, // f can cope with any lifetime 'b
    {
        let val = self.val.clone();
        consume(move |x| {
            *val.borrow_mut() = x;
            func(&*val.borrow())
        })
    }
}

据我所知,您的第二个示例至少存在另一个问题:您致电

self.stream.subscribe(move |x: S::Item| {
    *val.borrow_mut() = x;
    func(&*val.borrow());
})

subscribe接受一个接受借用的函数(即&S::Item,不是S::Item)。如果您传入参考,我不确定您是否/如何将其分配给val.borrow_mut. 您可能必须将其转换为自有值。

正如您已经提到的,您也可以设置Item = S::Item(不借用)。然而,这意味着您不能简单地传递val.borrow()func闭包内,因为它会从借来的值中移动。同样,解决方案可能是以某种方式将其转换为拥有的价值。


推荐阅读