arrays - Using generic iterators instead of specific list types -


i'm new rust, coming c# / java / similar.

in c# have ienumerable<t> can used iterate kind of array or list. c# has yield keyword can use return lazy list. here's example...

// lazily returns numbers out of enumerable ienumerable<int> evens(ienumerable<int> input) {     foreach (var x in input)     {         if (x % 2 == 0)         {             yield return x;         }     } } 

this silly example of course. know rust's map function, know how create own methods accept , return generic iterators.

from can gather, rust has generic iterators can use similarly, above understanding. see iter, intoiterator, iterator types, , more in documentation, no way understand them.

can provide clear examples of how create above? thank you!

p.s. lazy aspect optional. more concerned abstraction away specific list , array types.

first, forget intoiterator , other traits or types. core iteration trait in rust iterator. trimmed down definition follows:

trait iterator {     type item;  // type of elements returned iterator     fn next(&mut self) -> option<self::item>; } 

as know, can think of iterator cursor inside of structure. next() method advances cursor forward, returning element pointed @ previously. naturally, if collection exhausted, there nothing return, , next() returns option<self::item>, not self::item.

iterator trait, , can implemented specific types. note iterator itself not proper type can use return value or function argument - have use concrete types implement trait.

the above statement may sound restrictive - how use arbitrary iterator types then? - because of generics not so. if want function accept arbitrary iterators, make generic in corresponding argument, adding iterator bound on corresponding type parameter:

fn iterate_bytes<i>(iter: i) i: iterator<item=u8> { ... } 

returning iterators functions may difficult, see below.

for example, there method on &[t], called iter(), returns iterator yields references slice. iterator instance of this structure. can see on page how iterator implemented iter:

impl<'a, t> iterator iter<'a, t> {     type item = &'a t;     fn next(&mut self) -> option<&'a t> { ... }     ... } 

this structure holds reference original slice , iteration state inside it. next() method updates state , returns next value, if there any.

any value type implements iterator can used in for loop (for loop in fact works intoiterator, see below):

let s: &[u8] = b"hello"; b in s.iter() {     println!("{}", b);   // prints numerical value of each byte } 

now, iterator trait more complex above one. defines lot of transformation methods consume iterator called on , return new iterator somehow transforms or filters values original iterator. example, enumerate() method returns iterator yields values original iterator positional number of element:

let s: &[u8] = b"hello"; (i, b) in s.iter().enumerate() {     println!("{} @ {}", b, i);   // prints "x @ 0", "y @ 1", etc. } 

enumerate() defined this:

trait iterator {     type item;     ...     fn enumerate(self) -> enumerate<self> {         enumerate {             iter: self,             count: 0         }     }     ... } 

enumerate struct contains iterator , counter inside , implements iterator<item=(usize, i::item)>:

struct enumerate<i> {     iter: i,     count: usize }  impl<i> iterator enumerate<i> i: iterator {     type item = (usize, i::item);      #[inline]     fn next(&mut self) -> option<(usize, i::item)> {         self.iter.next().map(|a| {             let ret = (self.count, a);             self.count += 1;             ret         })     } } 

and this how iterator transformations implemented: each transformation wrapping struct wraps original iterator , implements iterator trait delegating original iterator , transforming resulting value somehow. example, s.iter().enumerate() example above returns value of type enumerate<iter<'static, u8>>.

note while enumerate() defined in iterator trait directly, can standalone function well:

fn enumerate<i>(iter: i) -> enumerate<i> i: iterator {     enumerate {         iter: iter,         count: 0     } } 

the method works - uses implicit self type parameter instead of explicitly named one.


you may wonder intoiterator trait is. well, convenience conversion trait can implemented type can converted iterator:

pub trait intoiterator self::intoiter::item == self::item {     type item;     type intoiter: iterator;      fn into_iter(self) -> self::intoiter; } 

for example, &'a [t] can converted iter<'a, t>, , has following implementation:

impl<'a, t> intoiterator &'a [t] {     type item = &'a t;     type intoiter = iter<'a, t>;      fn into_iter(self) -> iter<'a, t> {         self.iter()  // delegate existing method     } } 

this trait implemented container types , references these types. in fact used for loops - value of type implements intoiterator can used in in clause:

let s: &[u8] = b"hello"; b in s { ... } 

this nice learning , reading perspective because has less noise (in form of iter()-like methods). allows things these:

let v: vec<u8> = ...;  in &v { /* &u8 here, v borrowed immutably */ } in &mut v { /* &mut u8 here, v borrowed mutably */ } in v { /* u8 here, v consumed */ } 

this possible because intoiterator implemented differently &vec<t>, &mut vec<t> , vec<t>.

every iterator implements intoiterator performs identity conversion (into_iter() returns iterator called on), can use iterator instances in for loops well.

consequently, makes sense use intoiterator in generic functions because make api more convenient user. example, enumerate() function above rewritten such:

fn enumerate<i>(source: i) -> enumerate<i::intoiter> i: intoiter {     enumerate {         iter: source.into_iter(),         count: 0     } } 

now can see how generics can used implement transformations static typing easily. rust not have c#/python yield (but 1 of desired features, 1 day may appear in language!), need wrap source iterators explicitly. example, can write analogous above enumerate structure task want.

however, idiomatic way use existing combinators work you. example, code may written follows:

let iter = ...;  // iter implements iterator<item=i32> let r = iter.filter(|&x| x % 2 == 0);  // r implements iterator<item=i32> in r {     println!("{}", i);  // prints items iterator } 

however, using combinators may turn ugly when want write custom combinator functions because lot of existing combinator functions accept closures (e.g. filter() 1 above), closures in rust implemented values of anonymous types, there no way write signature of function returning iterator out:

fn filter_even<i>(source: i) -> ??? i: intoiter<item=i32> {     source.into_iter().filter(|&x| x % 2 == 0) } 

there several ways around this, 1 of them using trait objects:

fn filter_even<'a, i>(source: i) -> box<iterator<item=i32>+'a>     i: intoiterator<item=i32>, i::intoiter: 'a {     box::new(source.into_iter().filter(|&x| x % 2 == 0)) } 

here hide actual iterator type returned filter() behind trait object. note in order make function generic had add lifetime parameter , corresponding bound box trait object , i::intoiter associated type. necessary because i::intoiter may contain arbitrary lifetimes inside (just iter<'a, t> type above), , have specify them in trait object type (otherwise lifetime information lost).

trait objects created iterator trait implement iterator themselves, can continue using these iterators usual:

let source = vec![1_i32, 2, 3, 4]; in filter_even(source) {     println!("{}", i);  // prints 2 , 4 } 

Comments

Popular posts from this blog

javascript - Bootstrap Popover: iOS Safari strange behaviour -

Website Login Issue developed in magento -

Can the constants be defined inside a model file of a framework in PHP? -