gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 07:42:28 24 4
gpt4 key购买 nike

我想编写一个通用函数,它接受任何不可变借用的可迭代容器,例如数组、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>);
}

(省略了一些与我的问题无关的函数)

现在我的目标是编写一个观察器,它可以与任意可迭代容器一起使用,这些容器包含可以赋值的项目。它应该跟踪容器中项目的值(value)总和,因此保存当前总和和计算任何项目值(value)的函数。它应该实现 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 子句,则编译工作正常。

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

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 的借用都超出了范围。

最佳答案

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

关键是你不想&L实现IntoIterator<Item = &T>对于一个 生命周期但对于所有 潜在生命周期L可能碰巧有。

在这种情况下,您需要使用更高等级的特征绑定(bind):for<'a>将负责引入生命周期名称,同时向编译器发出信号,表明使用它的子句应该对 '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;
}
}

编译(至少在孤立的情况下)。

另见:

关于generics - 在 Rust 中编写一个将可迭代容器作为参数的通用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35940068/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com