Crate r4 [−] [src]
Provides a macro that generates an iterable sequence via for comprehensions (AKA list comprehensions).
If you're familiar with Python's list
comprehensions
or Scala's for
comprehensions,
this should be familiar. The use of the yield
keyword is also similar to
that of the recent generator syntax
RFC.
Examples
Sequences are built out of for
statements, which may be nested.
#[macro_use(iterate)] extern crate r4; let v: Vec<i32> = iterate![for x in 0..5; yield x * 2].collect(); assert_eq!(v, vec![0, 2, 4, 6, 8]);
Multiple for
statements are treated like nested loops.
#[derive(Debug, Eq, PartialEq)] struct Item { x: i32, y: i32, } let v: Vec<Item> = iterate![for x in 0..3; for y in 5..7; yield Item { x: x, y: y, }] .collect(); let mut w: Vec<Item> = Vec::new(); for x in 0..3 { for y in 5..7 { w.push(Item { x: x, y: y, }); } } assert_eq!(v, w);
Stand-alone values may be yield
ed.
let v: Vec<i32> = iterate![yield 0; yield 2; yield 4; yield 6; yield 8].collect(); assert_eq!(v, vec![0, 2, 4, 6, 8]);
Intermediate variables may be bound with let
, which may take a pattern.
let v: Vec<i32> = iterate![for x in 0..6; let y = x * 2; yield x * y].collect(); assert_eq!(v, vec![0, 2, 8, 18, 32, 50]); struct Item { x: i32, y: i32, } let v: Vec<i32> = iterate![for x in 0..3; for y in 5..7; let item = Item { x: x, y: y, }; let Item { x: a, y: b, } = item; yield a; yield b] .collect(); assert_eq!(v, vec![0, 5, 0, 6, 1, 5, 1, 6, 2, 5, 2, 6]);
Iteration may be short-circuited via if
.
let v: Vec<i32> = iterate![for x in 0..10; if x % 2 == 0; yield x].collect(); assert_eq!(v, vec![0, 2, 4, 6, 8]);
A pattern-based guard may be employed via if let
.
fn process(a: i32) -> Option<i32> { if a % 2 == 0 { Some(a) } else { None } } let v: Vec<i32> = iterate![for x in 0..10; if let Some(y) = process(x); yield y] .collect(); assert_eq!(v, vec![0, 2, 4, 6, 8]);
Expressions
See the macro for a full breakdown of expressions it recognizes. In brief, they are:
for x in xs
: introduces a new scope that iterates overxs
, binding the patternx
to each of its values.if cond
: short-circuits subsequent expressions ifcond
is nottrue
.if let pattern = expr
: introduces a new scope that bindspattern
toexpr
, short-circuiting subsequent expressions ifpattern
does not matchexpr
.let a = b
: introduces a new scope that bindsa
(which may be a pattern) tob
.yield r
: emits the value of the expressionr
.
Example expansion
This macro invocation:
#[macro_use(iterate)] extern crate r4; let items = iterate![for x in 0..10; let y = x + 1; if y % 2 == 0; let z = y + 3; for x_prime in z..(z + 3); yield x_prime];
is expanded like so:
extern crate r4; let items = { (0..10).flat_map(move |x| { let y = x + 1; r4::FlatIter::new(if y % 2 == 0 { Some({ let z = y + 3; (z..(z + 3)).flat_map(move |x_prime| { ::std::iter::once(x_prime) }) }) } else { None }) }) };
Lifetimes and moved values
You can see in the example expansion above that the closures which
iterate!
creates move variables that they refer to out of their
surrounding environment. This is done for lack of a better mechanism for
ensuring that the iterator created by iterate!
doesn't outlive values
referred to by the closures it generates. This makes it possible for an
iterate!
-derived macro to own its data, as in:
fn generate_values() -> Box<Iterator<Item=u32>> { let xs = vec![1, 2, 3, 4]; Box::new(iterate![for x in 0..10; if xs.contains(&x); yield 2 * x]) }
As a result, if you refer to a non-Copy
value from within an iterate!
macro, you will no longer be able to use that value.
fn generate_values() -> Box<Iterator<Item=u32>> { let xs = vec![1, 2, 3, 4]; let i = iterate![for x in 0..10; if xs.contains(&x); yield 2 * x]; // We already moved `xs` into the iterator above, so we can't use it again. println!("xs are: {:?}", xs); // Compilation error. Box::new(i) }
This behavior can be circumvented by creating a borrow yourself, although this will restrict the lifetime of the iterator to that of the borrow.
let xs = vec![1, 2, 3, 4]; let borrow = &xs; // The iterators `i` and `j` are both limited to the lifetime of `borrow`, // but they can share use of its data. let i = iterate![for x in 0..10; if borrow.contains(&x); yield 2 * x]; let j = iterate![for x in 2..20; if borrow.contains(&x); yield 2 * x]; let v: Vec<i32> = i.chain(j).collect(); assert_eq!(v, vec![2, 4, 6, 8, 4, 6, 8]);
Macros
iterate |
Produces an iterator over values |
Structs
FlatIter |
Flat-maps an Option<I> where I: Iterator into the underlying iterator. You
probably don't need to use this type directly. It is used internally by the
|