在Rust中编写一个将可迭代容器作为参数的泛型函数 [英] Writing a generic function that takes an iterable container as parameter in Rust

查看:87
本文介绍了在Rust中编写一个将可迭代容器作为参数的泛型函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个通用函数,该函数接受任何不可变借用的可迭代容器,例如数组,VecBTreeSet等.由于此函数是我要实现的特征的一部分,因此我无法更改它的签名,因此不可能直接将迭代器作为参数,而且我也不能在函数签名中引入任何生存期参数.

I want to write a generic function that takes any immutably borrowed iterable container such as an array, Vec, BTreeSet, etc. Since this function is part of a trait that I am implementing, I am not able to change the signature of it, so it's not possible to directly take an iterator as parameter and I also can't introduce any lifetime parameters to the function signature.

我试图在Rust中实现观察者模式.可观察者和观察者的外观如下:

I tried to implement the observer pattern in Rust. The observable and the observer look as follows:

struct Observable<T> {
    value: T,
}

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

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

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

(Some functions that were irrelevant to my problem are omitted)

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

It is now my objective to write an observer that can be used with arbitrary iterable containers which hold items that can be assigned a value. It is supposed to keep track of the sum of values of the items in the container and therefore holds the current sum and a function that calculates the value of any item. It should implement the Observer trait so the sum can be updated each time the container changes.

use std::cell::RefCell;

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

到目前为止的方法

我尝试使update函数能够编译相当长时间,但未成功.以下是我尝试过的函数的版本之一:

Approaches so far

I have unsuccessfully tried to get the update function to compile for quite some time. The following is one of the versions of the function that I tried:

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的寿命可能不够长:

However, the compiler complains that both parameter types T and L might not live long enough:

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子句,则编译有效.

The error message even stays the same if the whole function body is commented out. If I also remove the where-clause, the compilation works.

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

If I follow the compiler's suggestion to add explicit lifetime bounds to the parameter types:

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借用都超出范围.

I don't understand the problem with lifetimes in this function. At any point where this function is called, the compiler should make sure that the borrow of observable lasts at least until the function returns. At that time, any borrow of observable has gone out of scope.

推荐答案

这是较高特质界限(HRTB)的一种情况.

This is a case for Higher Ranked Trait Bounds (HRTB).

重点是,您不希望&L one 的生命周期中实现IntoIterator<Item = &T>,而是希望在 all 可能发生的 all 潜在生命周期中实现.

The point is that you do not want &L to implement IntoIterator<Item = &T> for one lifetime but for all potential lifetimes that L may happen to have.

在这种情况下,您需要使用较高等级的特质界限:for<'a>将负责引入生命周期名称,同时向编译器发出信号,告知使用该子句的子句对于'a的所有可能值均有效

In this case, you need to use a Higher Ranked Trait Bound: for<'a> will take care of introducing the lifetime name whilst simultaneously signaling to the compiler that the clause using it should be valid for all possible values of '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;
    }
}

(至少是单独编译).

另请参阅:

这篇关于在Rust中编写一个将可迭代容器作为参数的泛型函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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