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

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

问题描述

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

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 中实现观察者模式.observable 和observer 看起来如下:

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).

关键是您不希望 &Lone 生命周期内实现 IntoIterator所有 L 可能碰巧拥有的潜在生命周期.

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;
    }
}

编译(至少是独立的).

which compiles (at least in isolation).

另见:

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

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