Написание универсальной функции, которая принимает итеративный контейнер в качестве параметра в 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;
    }
}

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

Другие вопросы по тегам