Явное время жизни для себя в признаке, похоже, приводит к тому, что «E0499 не может заимствовать `*emitter` как изменяемый более одного раза» в цикле

Я столкнулся с проблемой с признаком, который может возвращать ссылку на данные, находящиеся в self. Вот минимальный пример:

trait A<T> {
    fn emit_symbol(&mut self) -> T;
}

fn via_ans<'a>(emitter: &'a mut impl A<char>) -> String {
    let mut rval = String::new();
    for _ in 0..30 {
        let ch = emitter.emit_symbol();
        rval.push(ch)
    }
    rval
}

struct Hippo<T> {
    things: Vec<T>,
}

impl<T> A<&T> for Hippo<T> {
    fn emit_symbol(&mut self) -> &T {
        &self.things[2]
    }
}

выдает ошибку

error: `impl` item signature doesn't match `trait` item signature
  --> src/b.rs:19:5
   |
2  |     fn emit_symbol(&mut self) -> T;
   |     ------------------------------- expected `fn(&'1 mut b::Hippo<T>) -> &'2 T`
...
19 |     fn emit_symbol(&mut self) -> &T {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 mut b::Hippo<T>) -> &'1 T`
   |
   = note: expected signature `fn(&'1 mut b::Hippo<T>) -> &'2 T`
              found signature `fn(&'1 mut b::Hippo<T>) -> &'1 T`
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
  --> src/b.rs:2:34
   |
2  |     fn emit_symbol(&mut self) -> T;
   |                                  ^ consider borrowing this type parameter in the trait

Если я добавлю время жизни к признаку для &'s self, то реализация будет работать, но я столкнусь с новой ошибкой в ​​цикле, который использует метод признака.

trait A<'s, T> {
    fn emit_symbol(&'s mut self) -> T;
}

fn via_ans<'a: 'b, 'b>(emitter: &'a mut impl A<'b, char>) -> String {
    let mut rval = String::new();
    for _ in 0..30 {
        let ch = emitter.emit_symbol();
        rval.push(ch)
    }
    rval
}

struct Hippo<T> {
    things: Vec<T>,
}

impl<'s, T> A<'s, &'s T> for Hippo<T> {
    fn emit_symbol(&'s mut self) -> &'s T {
        &self.things[2]
    }
}

выдает ошибку

error[E0499]: cannot borrow `*emitter` as mutable more than once at a time
  --> src/main.rs:10:18
   |
7  | fn via_ans<'a: 'b, 'b>(emitter: &'a mut impl A<'b, char>) -> String {
   |                    -- lifetime `'b` defined here
...
10 |         let ch = emitter.emit_symbol();
   |                  ^^^^^^^--------------
   |                  |
   |                  `*emitter` was mutably borrowed here in the previous iteration of the loop
   |                  argument requires that `*emitter` is borrowed for `'b`

Является ли это недостатком средства проверки заимствований Rust или происходит что-то еще?

Как я могу настроить эту особенность для работы в моем приложении?

Самое простое решение вашей проблемы — использовать второе определение признака, но опустить mut в определении emit_symbol.

cafce25 24.04.2024 17:26

Мут необходим, поскольку некоторые реализации изменяют свое внутреннее состояние. Я исключил их из примера, потому что они немного сложны и отвлекают от основной проблемы.

Mutant Bob 24.04.2024 17:30

@eqqyal ах, HRTB делает эту работу, а не GAT но хорошая мысль! Я совершенно скучал по тому, что они собираются в String, а не в Vec<&T>

cafce25 24.04.2024 17:57
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
3
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема с вашим вторым подходом заключается в том, что, поскольку impl A<'b, char> является параметром вашей функции, компилятор может выбрать только одно время жизни для 'b, и поскольку оно передается извне функции, это время жизни также длится для всей вашей функции. Поэтому, когда вы заимствуете emitter при первом вызове emit_symbol, этот заимствование не освобождается до завершения функции.

Это типичное приложение для Границы характеристик более высокого ранга (HRTB):

fn via_ans<'a>(emitter: &'a mut impl for<'b> A<'b, char>) -> String {
    let mut rval = String::new();
    for _ in 0..30 {
        let ch = emitter.emit_symbol();
        rval.push(ch)
    }
    rval
}

поскольку теперь это означает, что emitter должен быть типа, который реализует A для каждого времени жизни, чтобы компилятор мог выбирать другой тип для каждой итерации цикла.

На самом деле ваша проблема почти такая же, которую serde иногда приходится решать с помощью своей черты Deserialize, чтобы скрыть не очень красивый HRTB, они добавляют дополнительную черту DeserializeOwned, которую вы также можете применить к своему случаю:

trait AOwned<T>: for<'a> A<'a, T> {}
impl<S, T> AOwned<T> for S where S: for<'a> A<'a, T> {}
fn via_ans<'a>(emitter: &'a mut impl AOwned<char>) -> String {
    //…
}

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