Написание общей функции, которая принимает итерируемый контейнер в качестве параметра в 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, чтобы сумма могла обновляться каждый раз, когда контейнер изменяется.

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-clause, компиляция работает.

Если я следую предложению компиляторов для добавления явных ограничений срока службы к типам параметров, компилятор выдаст следующий результат:

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() {
                                               ^~~

Затем он предлагает добавить явный параметр lifetime к параметру 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;
    }
}

который компилируется (по крайней мере изолированно).