2015-05-13 2 views
27

Как кто-то реализует признаки Iterator и IntoIterator для следующих структур?Как реализовать Итератор и IntoIterator для простой структуры?

struct Pixel { 
    r: i8, 
    g: i8, 
    b: i8, 
} 

Я пробовал различные формы следующего без успеха.

impl IntoIterator for Pixel { 
    type Item = i8; 
    type IntoIter = Iterator<Item=Self::Item>; 

    fn into_iter(self) -> Self::IntoIter { 
     [&self.r, &self.b, &self.g].into_iter() 
    } 
} 

Этот код дает мне ошибку компиляции

error[E0277]: the trait bound `std::iter::Iterator<Item=i8> + 'static: std::marker::Sized` is not satisfied 
--> src/main.rs:7:6 
    | 
7 | impl IntoIterator for Pixel { 
    |  ^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=i8> + 'static` 
    | 
    = note: `std::iter::Iterator<Item=i8> + 'static` does not have a constant size known at compile-time 
    = note: required by `std::iter::IntoIterator` 

ответ

37

Ваш тип итератора Iterator<Item = Self::Item>, но Iterator это черта. Черты реализованы структурами, они не существуют сами по себе. У вас также может быть объект ссылочного объекта (&Iterator) или объект объекта с боксами (Box<Iterator>), оба из которых имеют известные размеры.

Вместо этого мы создаем PixelIntoIterator, который имеет известный размер и реализуютIterator сами:

struct Pixel { 
    r: i8, 
    g: i8, 
    b: i8, 
} 

impl IntoIterator for Pixel { 
    type Item = i8; 
    type IntoIter = PixelIntoIterator; 

    fn into_iter(self) -> Self::IntoIter { 
     PixelIntoIterator { pixel: self, index: 0 } 
    } 
} 

struct PixelIntoIterator { 
    pixel: Pixel, 
    index: usize, 
} 

impl Iterator for PixelIntoIterator { 
    type Item = i8; 
    fn next(&mut self) -> Option<i8> { 
     let result = match self.index { 
      0 => self.pixel.r, 
      1 => self.pixel.g, 
      2 => self.pixel.b, 
      _ => return None, 
     }; 
     self.index += 1; 
     Some(result) 
    } 
} 

fn main() { 
    let p = Pixel { r: 54, g: 23, b: 74 }; 
    for component in p { 
     println!("{}", component); 
    } 
} 

Это имеет хорошую пользу возвращения фактических i8 с, а не ссылками. Поскольку они настолько малы, вы можете передать их напрямую.

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

impl<'a> IntoIterator for &'a Pixel { 
    type Item = i8; 
    type IntoIter = PixelIterator<'a>; 

    fn into_iter(self) -> Self::IntoIter { 
     PixelIterator { pixel: self, index: 0 } 
    } 
} 

struct PixelIterator<'a> { 
    pixel: &'a Pixel, 
    index: usize, 
} 

impl<'a> Iterator for PixelIterator<'a> { 
    type Item = i8; 
    fn next(&mut self) -> Option<i8> { 
     let result = match self.index { 
      0 => self.pixel.r, 
      1 => self.pixel.g, 
      2 => self.pixel.b, 
      _ => return None, 
     }; 
     self.index += 1; 
     Some(result) 
    } 
} 

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

+1

OP был чем-то вроде: было бы гораздо удобнее написать это, повторно используя итераторы, которые уже существуют, например, с '[T; 3] '. Но AFAIK, вы не можете выходить из массивов. Вместо этого вы можете это сделать, но это стоит выделить: http://is.gd/IMVLoG – BurntSushi5

+1

Правильно, и вы не можете преобразовать массив в итератор, чтобы сохранить его автономным. Выделение 'Vec', безусловно, является опцией, но, похоже, излишним для * этой * структуры. – Shepmaster

+0

Теперь, когда у нас есть хороший ответ, я отправлю свое. Он немного догадывается о массивах. – ArtemGr

2

Во-первых, IntoIter должен указывать на реальный struct, а не к trait для того, чтобы Руст, чтобы иметь возможность передать значение вокруг (это то, что Sized средства). В случае массивов into_iter возвращает std::slice::Iterstruct.

Во-вторых, типичный массив, [1, 2, 3], не выделяется на кучу. Фактически, компилятору разрешено полностью исключить выделение, вместо этого указывая на предварительно скомпилированный массив. Возможность перебора массивов без их копирования в любом месте я думаю, причина, почему реализация IntoIterator для массивов не переместить массив в любом месте, как и другие IntoIterator реализации. Вместо этого представляется ссылкой существующего массива. Вы можете видеть из its signature

impl<'a, T> IntoIterator for &'a [T; 3] 
    type Item = &'a T 
    type IntoIter = Iter<'a, T> 
    fn into_iter(self) -> Iter<'a, T> 

, что он принимает ссылку на массив (&'a [T; 3]).

Таким образом, вы не можете использовать его так, как вы пытаетесь. Связанный массив должен пережить возвращенный итератор. Here's a version, где компилятор Rust сообщает об этом.

Вектор имеет реализацию IntoIterator, которая действительно перемещает данные в итератор и так you can use it.


P.S. Для того, чтобы сделать это как быстро и просто, возвращают массив вместо итератора (playpen):

impl Pixel { 
    fn into_array(self) -> [i8; 3] {[self.r, self.g, self.b]} 
} 

Таким образом, массив первых переехал во внешнюю сферу, а затем он может быть ссылка от внешнего Итератор области:

for color in &(Pixel {r: 1, g: 2, b: 3}).into_array() { 
    println! ("{}", color); 
} 
+0

Я не знаю достаточно о языке, чтобы понять любые дополнительные последствия, которые могут возникнуть с этим, но он выглядит чистым. Спасибо. –

+1

Использование 'vec!' Добавляет распределение кучи, оно медленнее, чем версия Shepmaster. На моей машине версия Shepmaster (http://is.gd/BJUSbZ) выполняет в ** 1 ns/iter **. 'vec!' версия (http://is.gd/CMNqzR) выполняет в ** 23 нс/итер **. Версия, возвращающая массив (http://is.gd/pr6Zaf), быстро и просто реализуется, она работает на ** 1 ns/iter ** на моем оборудовании. – ArtemGr