简体   繁体   English

在 Rust 中编写一个以可迭代容器作为参数的通用函数

[英]Writing a generic function that takes an iterable container as parameter in Rust

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.我想编写一个通用函数,它采用任何不可改变的借用可迭代容器,例如数组、 VecBTreeSet等。由于此函数是我正在实现的特征的一部分,因此我无法更改它的签名,所以不能直接将迭代器作为参数,而且我也不能向函数签名中引入任何生命周期参数。

Context语境

I tried to implement the observer pattern in Rust.我尝试在 Rust 中实现观察者模式。 The observable and the observer look as follows: observable 和观察者如下所示:

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) (省略了一些与我的问题无关的功能)

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.它应该实现Observer特性,以便每次容器更改时都可以更新总和。

use std::cell::RefCell;

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

Approaches so far到目前为止的方法

I have unsuccessfully tried to get the update function to compile for quite some time.我尝试编译update函数已经有一段时间了,但没有成功。 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;
    }
}

However, the compiler complains that both parameter types T and L might not live long enough:但是,编译器抱怨参数类型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 | | }
   | |_^

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.如果我还删除了where子句,则编译工作。

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>

The compiler gives the following error:编译器给出以下错误:

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

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.在调用此函数的任何时候,编译器都应确保observable的借用至少持续到函数返回为止。 At that time, any borrow of observable has gone out of scope.那时,任何对observable借用都超出了范围。

This is a case for Higher Ranked Trait Bounds (HRTB).这是高排名特征边界 (HRTB) 的一个案例。

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.关键是您不希望&L一个生命周期内实现IntoIterator<Item = &T> ,而是在L可能碰巧拥有的所有潜在生命周期内实现。

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 .在这种情况下,您需要使用更高排名的 Trait Bound: for<'a>将负责引入生命周期名称,同时向编译器发出信号,使用它的子句应该对'a所有可能值都有效。

This means:这意味着:

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).它编译(至少在隔离中)。

See also:也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 新类型作为 Rust 中的通用参数? - Newtype as generic parameter in Rust? rust:定义一个通用函数,该函数将接受对实现特定特征的任何可迭代事物的借用 - rust: Define a generic function that will accept a borrow to any iterable of things that implement a certain trait 如何仅使用一个类型参数在 Rust 中定义通用 function - How to use only one type parameter to define a generic function in Rust Rust 通用 linspace function - Rust generic linspace function 如何编写一个将字符串或错误作为参数的通用 function? - How to write a generic function that takes a string or an error as a parameter? Dart 通用 function 用于 Iterable<num></num> - Dart generic function for Iterable<num> 如何调用GetMethod获取带有泛型参数的泛型函数(不使用GetMethods)? - How do you call GetMethod for a generic function that takes a generic parameter (without using GetMethods)? 实现将通用接口作为参数的通用接口 - Implementing generic interface that takes a generic interface as a parameter 是否可以编写一个通用的 rust function 告诉您给定的枚举变体是否在容器中? - Is it possible to write a generic rust function telling you if a given enum variant is in a container? 可迭代<iterable<t> &gt; 无法确认 function 中的通用 T </iterable<t> - Iterable<Iterable<T>> cannot confirm generic T in function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM