2016-01-15 2 views
2

Я определяю черту, которая принимает параметр i: &I. Я хотел бы использовать это значение i в цикле for.Написание реализации общего признака с помощью признака IntoIterator, но только для неизменяемых эталонных разработчиков

Например:

struct Test; 

trait Bar<I> { 
    fn bar(&self, i: &I); 
} 

impl<T, I: IntoIterator<Item=T>> Bar<I> for Test { 
    fn bar(&self, i: &I) { 
     for x in i { 
      println!("woo!"); 
     } 
    } 
} 

fn main() { 
    let vec = vec!(1, 2, 3); 
    let test = Test; 
    test.bar(&vec); 
} 

Playground link

Это приводит к ошибке:

<anon>:10:9: 12:10 error: the trait `core::iter::Iterator` is not implemented for the type `&I` [E0277] <anon>:10   for x in i { <anon>:11    println!("woo!"); <anon>:12   } <anon>:10:9: 12:10 help: see the detailed explanation for E0277 <anon>:10:9: 12:10 note: `&I` is not an iterator; maybe try calling `.iter()` or a similar method <anon>:10:9: 12:10 note: required by `core::iter::IntoIterator::into_iter` error: aborting due to previous error playpen: application terminated with error code 101 

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

Я бы очень хотел сохранить неизменяемую ссылку в определении функции, поскольку эта черта пытается быть общей для многих типов, а определение других реализаций с использованием Bar<&'a I> привело к некоторым другим связанным с жизнью проблемам, которые у меня были проблемы с.

ответ

7

Сказать, что I является IntoIterator ничего не говорит о &I, например. x..y - IntoIterator (потому что это Iterator и все они), но &(x..y) нет.

Вы специально хотите связать &I, который, к счастью, может быть выполнен с помощью предложения where, например.

impl<I, T> Bar<I> for Test 
    where for<'a> &'a I: IntoIterator<Item = T> 
{ 
    fn bar(&self, i: &I) { 
     for x in i { 
      println!("woo!"); 
     } 
    } 
} 

for<'a> просто означает, что «для любой жизни 'a», и поэтому положение where говорит, что &I всегда является IntoIterator (просто писать where &I: IntoIterator не вполне достаточно).

Есть некоторые варианты, которые можно сделать относительно параметра T, например.

  1. IntoIterator<Item = T>
  2. IntoIterator<Item = &'a T>
  3. удалить параметр целиком и просто написать IntoIterator

Лучший выбор будет зависеть от того, что вы делаете с ним. Для конкретного примера в вопросе я бы пошел с 3, так как тип Item вообще не имеет значения. Число 2 имеет смысл, потому что почти все типы, которые имеют &T, реализуют IntoIterator, будут давать ссылки (это также, по-видимому, позволяет избежать большинства ошибок/общих трудностей, которые в настоящее время у компилятора есть о рассуждениях об универсальном количественном определении за время жизни, которые достигают 1 и 3).

2

В Rust, by convention, методы, чье имя начинается с into, принимают свой аргумент по значению и преобразуют его в другое значение, обычно повторно используя некоторые ресурсы из исходного значения. Знак IntoIterator и его into_iter следуют этому соглашению.

Вы уверены, что вам нужно использовать &I в свойстве? Ваши коды отлично работают с I.Это потому, что есть реализация IntoIterator для &Vec<T>. Вот почему можно написать for x in &v, где v - это Vec.

struct Test; 

trait Bar<I> { 
    fn bar(&self, i: I); 
} 

impl<T, I: IntoIterator<Item=T>> Bar<I> for Test { 
    fn bar(&self, i: I) { 
     for x in i { 
      println!("woo!"); 
     } 
    } 
} 

fn main() { 
    let vec = vec!(1, 2, 3); 
    let test = Test; 
    test.bar(&vec); 
} 

playground