【问题标题】:Writing a generic function that takes an iterable container as parameter in Rust在 Rust 中编写一个将可迭代容器作为参数的通用函数
【发布时间】:2016-06-26 16:32:35
【问题描述】:

我想编写一个通用函数,它接受任何不可变借用的可迭代容器,例如数组、VecBTreeSet 等。由于这个函数是我正在实现的特征的一部分,所以我无法改变它的签名,所以不能直接将迭代器作为参数,我也不能在函数签名中引入任何生命周期参数。

上下文

我尝试在 Rust 中实现观察者模式。 observable 和observer 看起来如下:

struct Observable<T> {
    value: T,
}

impl<T> Observable<T> {
    pub fn get(&self) -> &T {
        &self.value
    }
}

trait Observer<T> {
    fn update(&self, &Observable<T>);
}

(省略了一些与我的问题无关的功能)

现在我的目标是编写一个观察者,该观察者可以与任意可迭代容器一起使用,其中包含可以分配值的项目。它应该跟踪容器中项目的值的总和,因此保存当前总和和一个计算任何项目值的函数。它应该实现 Observer 特征,以便每次容器更改时都可以更新总和。

use std::cell::RefCell;

struct SumObserver<T> {
    current_sum: RefCell<i64>,
    get_value: Fn(&T) -> i64,
}

目前的方法

我尝试编译update 函数已经有一段时间了。以下是我尝试过的功能版本之一:

impl<'a, T, L> Observer<L> for SumObserver<T>
where
    &'a L: IntoIterator<Item = &'a T>,
{
    fn update(&self, observable: &Observable<L>) {
        let mut sum: i64 = 0;
        for item in observable.get() {
            sum += (self.get_value)(item);
        }
        *self.current_sum.borrow_mut() = sum;
    }
}

但是,编译器抱怨TL 两种参数类型的寿命可能不够长:

error[E0309]: the parameter type `T` may not live long enough
  --> src/lib.rs:22:1
   |
22 |   impl<'a, T, L> Observer<L> for SumObserver<T>
   |   ^        - help: consider adding an explicit lifetime bound `T: 'a`...
   |  _|
   | |
23 | | where
24 | |     &'a L: IntoIterator<Item = &'a T>,
25 | | {
...  |
32 | |     }
33 | | }
   | |_^
   |
note: ...so that the reference type `&'a T` does not outlive the data it points at
  --> src/lib.rs:22:1
   |
22 | / impl<'a, T, L> Observer<L> for SumObserver<T>
23 | | where
24 | |     &'a L: IntoIterator<Item = &'a T>,
25 | | {
...  |
32 | |     }
33 | | }
   | |_^

如果整个函数体被注释掉,错误消息甚至保持不变。如果我也删除where-clause,编译工作。

如果我按照编译器的建议为参数类型添加显式生命周期:

impl<'a, T: 'a, L: 'a> Observer<L> for SumObserver<T>

编译器给出以下错误:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:28:32
   |
28 |         for item in observable.get() {
   |                                ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 26:5...
  --> src/lib.rs:26:5
   |
26 | /     fn update(&self, observable: &Observable<L>) {
27 | |         let mut sum: i64 = 0;
28 | |         for item in observable.get() {
29 | |             sum += (self.get_value)(item);
30 | |         }
31 | |         *self.current_sum.borrow_mut() = sum;
32 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:28:21
   |
28 |         for item in observable.get() {
   |                     ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 22:6...
  --> src/lib.rs:22:6
   |
22 | impl<'a, T: 'a, L: 'a> Observer<L> for SumObserver<T>
   |      ^^
   = note: ...so that the types are compatible:
           expected std::iter::IntoIterator
              found std::iter::IntoIterator

我不明白这个函数的生命周期问题。在调用此函数的任何时候,编译器都应确保observable 的借用至少持续到函数返回。那时,observable 的任何借用都超出了范围。

【问题讨论】:

    标签: generics iterator rust lifetime


    【解决方案1】:

    这是更高等级特征边界 (HRTB) 的情况。

    关键是您不希望&amp;L一个 生命周期内实现IntoIterator&lt;Item = &amp;T&gt;,而是在L 可能恰好拥有的所有 个潜在生命周期内实现。

    在这种情况下,您需要使用更高等级的特征绑定:for&lt;'a&gt; 将负责引入生命周期名称,同时向编译器发出信号,使用它的子句应该对 'a 的所有可能值有效.

    这意味着:

    impl<T, L> Observer<L> for SumObserver<T>
    where
        for<'a> &'a L: IntoIterator<Item = &'a T>,
    {
        fn update(&self, observable: &Observable<L>) {
            let mut sum: i64 = 0;
            for item in observable.get() {
                sum += (self.get_value)(item);
            }
            *self.current_sum.borrow_mut() = sum;
        }
    }
    

    编译(至少是独立的)。

    另见:

    【讨论】:

      猜你喜欢
      • 2016-04-30
      • 2020-09-28
      • 2019-06-20
      • 2012-01-11
      • 2021-11-08
      • 2013-12-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多