Итератор возвращает предметы по ссылке, срок службы

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

struct Foo {
   d: [u8; 42],
   pos: usize
}

impl<'a> Iterator<&'a u8> for Foo {
   fn next<'a>(&'a mut self) -> Option<&'a u8> {
      let r = self.d.get(self.pos);
      if r.is_some() {
         self.pos += 1;
      }
      r
   }
}

fn main() {
   let mut x = Foo {
      d: [1; 42],
      pos: 0
   };

   for i in x {
      println!("{}", i);
   }
}

Однако этот код не компилируется должным образом, я получаю проблему, связанную с временем жизни параметров, вот соответствующая ошибка:

$ rustc test.rs
test.rs:8:5: 14:6 error: method `next` has an incompatible type for trait: expected concrete lifetime, but found bound lifetime parameter
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> {
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() {
test.rs:11             self.pos += 1;
test.rs:12         }
test.rs:13         r
           ...
test.rs:8:49: 14:6 note: expected concrete lifetime is the lifetime 'a as defined on the block at 8:48
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> {
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() {
test.rs:11             self.pos += 1;
test.rs:12         }
test.rs:13         r
           ...
error: aborting due to previous error

Есть ли у кого-нибудь идея, как исправить эту проблему и все еще возвращать элементы по ссылке?

По крайней мере, что означает это сообщение: ожидаемое конкретное время жизни, но найденный параметр связанного срока жизни?

Ответ 1

Примечание по используемой версии Rust:, когда этот вопрос и ответ были написаны, черта Iterator использовала дженерики; он изменил использование связанных типов и теперь определяется таким образом:

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    …
}

И поэтому приведенная здесь некорректная реализация будет выглядеть следующим образом:

impl<'a> Iterator for Foo {
    type Item = &'a u8;

    fn next<'a>(&'a mut self) -> Option<&'a u8>;
}

В практическом плане это ничего не влияет; просто A становится Self::Item.

Определение признака Iterator таково:

pub trait Iterator<A> {
    fn next(&mut self) -> Option<A>;
    …
}

Примечание внимательно: fn next(&mut self) -> Option<A>.

Вот что у вас есть:

impl<'a> Iterator<&'a u8> for Foo {
    fn next<'a>(&'a mut self) -> Option<&'a u8>;
}

Примечание внимательно: fn next<'a>(&'a mut self) -> Option<&'a u8>.

Здесь есть несколько проблем:

  • Вы ввели новый общий параметр <'a>, которого не должно быть. Для удобства и для того, чтобы подчеркнуть, что здесь произошло, я буду дублировать 'a, определенный в блоке impl и p 'a, определенном на методе ρ₁. Они не то же самое.

  • Время жизни &mut self отличается от времени жизни.

  • Время жизни возвращаемого типа отличается от черты: где A - &'ρ₀ u8, тип возврата используется вместо A &'ρ₁ u8. Он ожидал конкретного времени жизни ρ₀, но вместо этого нашел время жизни ρ₁. (Я не уверен точно, что означает бит "связанный", поэтому я буду молчать, чтобы я не ошибся.)

Вот что это значит: вы не можете связать время жизни объекта, который вы перебираете, на &mut self. Вместо этого он должен быть связан с чем-то в типе, для которого вы реализуете черту. Например, повторение элементов в срезе выполняется путем создания нового объекта итератора, подключенного к базовому фрагменту, impl<'a, T> Iterator<&'a T> for Items<'a, T>. Выраженный иначе, то, как создаются итерационные черты, не является, если вы создаете ссылки, для того, чтобы вы возвращали что-то внутри self, а скорее возвращали что-то внутри другого объекта, на который вы ссылаетесь.

Для вашего конкретного, предположительно простого примера, вы должны либо прекратить давать ссылки, либо изменить его так, чтобы ваш объект итератора не содержал данные, которые вы итерируете, пусть он просто содержит ссылку на него, например. &'a [T] или даже что-то вроде Items<'a, T>.