Написание универсальной функции, которая принимает итеративный контейнер в качестве параметра в Rust
Я хочу написать универсальную функцию, которая принимает любой неизменяемый заемный итеративный контейнер, такой как массив, Vec
, BTreeSet
и т. д. Так как эта функция является частью черты, которую я реализую, я не могу изменить ее сигнатуру, поэтому невозможно напрямую взять итератор в качестве параметра, и я также не могу ввести какие-либо параметры времени жизни для подпись функции.
контекст
Я попытался реализовать шаблон наблюдателя в Rust. Наблюдаемое и наблюдатель выглядят следующим образом:
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
trait, поэтому сумма может обновляться каждый раз при изменении контейнера.
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;
}
}
Однако компилятор жалуется, что оба типа параметров T
а также L
может не прожить достаточно долго (E309). Сообщение об ошибке остается неизменным, если все тело функции закомментировано. Если я также удалю предложение where, компиляция работает.
Если я последую предложению компилятора добавить явные границы времени жизни для типов параметров, компилятор выдаст следующий вывод:
src/main.rs:25:26: 25:29 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495]
src/main.rs:25 for item in observable.get() {
^~~
Затем предлагается добавить явный параметр времени жизни к observable
параметр, который не возможен, так как это изменило бы сигнатуру функции, которая требуется чертой.
Я не понимаю проблемы с продолжительностью жизни в этой функции. В любой точке, где вызывается эта функция, компилятор должен убедиться, что заимствование observable
длится по крайней мере, пока функция не вернется. В то время, любой заем observable
вышел за рамки.
1 ответ
Это относится к более высоким ранговым границам продолжительности жизни (HRLB).
Дело в том, что ты не хочешь &L
реализовать IntoIterator<Item=&T>
на одну жизнь, но для всех потенциальных жизней, которые L
может случиться, чтобы иметь.
В этом случае вам нужно использовать более высокий рейтинг пожизненного уровня: 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;
}
}
который компилируется (по крайней мере, в изоляции).