Merge branch 'release/1.0.0'
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
[package]
|
||||
name = "r4"
|
||||
version = "1.0.0-b1"
|
||||
version = "1.0.0"
|
||||
authors = [ "Stu Black <trurl@freeshell.org>" ]
|
||||
description = "A compact macro that generates iterators using for comprehensions and natural Rust syntax."
|
||||
documentation = "https://docs.rs/r4/1.0.0"
|
||||
repository = "https://github.com/dstu/r4"
|
||||
keywords = ["iterator", "iterate", "for", "comprehension", "macro"]
|
||||
license = "Apache-2.0"
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
27
README.md
Normal file
27
README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# r4: for comprehensions for Rust
|
||||
|
||||
This package provides the `iterate!` macro, which builds for comprehensions out
|
||||
of nested flat-map operations. If you're familiar with Python's list
|
||||
comprehensions or Scala's for statement, the syntax should be familiar.
|
||||
|
||||
See rustdocs for usage, examples, and a more detailed description of the macro's
|
||||
internals.
|
||||
|
||||
# To-do
|
||||
- Examine overhead introduced by nesting closures instead of using naked loops.
|
||||
- Benchmark.
|
||||
- ?Figure out how to avoid moving values we don't have to.
|
||||
- ?Figure out how to avoid creating some new iterators unnecessarily.
|
||||
|
||||
# Copyright
|
||||
|
||||
Copyright 2015-2018, Donald S. Black.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
95
README.org
95
README.org
@@ -1,95 +0,0 @@
|
||||
* r4: for comprehensions for Rust
|
||||
|
||||
This package provides the =iterate!= macro, which builds for comprehensions out
|
||||
of nested flat-map operations. If you're familiar with Python's list
|
||||
comprehensions or Scala's for statement, the syntax should be familiar.
|
||||
|
||||
The macro should be provided with a series of semicolon-separated statements,
|
||||
ending with a =yield= statement. Permitted statements:
|
||||
- =for x in xs= :: Introduces a scope that iterates through =xs= and binds =x=
|
||||
to each element thereof. Successive =for= statements behave
|
||||
like nested loops, so =iterate![for x in xs; for y in ys; yield (x, y)]=
|
||||
will consist of =xs.len() * ys.len()= elements.
|
||||
- =let x = y= :: Binds =x= (which may be a pattern, like =Some(foo)=) to the
|
||||
expression =y=.
|
||||
- =if a= :: Skips over any later statements and doesn't yield anything iff the
|
||||
boolean expression =a= isn't true.
|
||||
- =yield x= :: Yields the given expression for an iteration.
|
||||
|
||||
The closures created by =iterate!= move values out of the surrounding
|
||||
environment. This means that you could have trouble like this:
|
||||
|
||||
#+BEGIN_SRC rust
|
||||
#[macro_use]
|
||||
extern crate r4;
|
||||
|
||||
let values = vec![1, 2, 3];
|
||||
for x in iterate![for i in 0..10;
|
||||
for v in values; // This is evaluated inside of a closure
|
||||
// that moves values out of the above binding.
|
||||
yield v] {
|
||||
println!("{}", x)
|
||||
}
|
||||
// values was moved and can't be used.
|
||||
println!("{}", values.len())
|
||||
#+END_SRC
|
||||
|
||||
This is done for lack of a better mechanism for ensuring that the iterator
|
||||
created by =iterate!= doesn't outlive any values referred to by the closures it
|
||||
generates.
|
||||
|
||||
** Example expansion
|
||||
This macro invocation:
|
||||
|
||||
#+BEGIN_SRC rust
|
||||
#[macro_use]
|
||||
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];
|
||||
#+END_SRC
|
||||
|
||||
is expanded like so:
|
||||
|
||||
#+BEGIN_SRC rust
|
||||
#[macro_use]
|
||||
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| {
|
||||
Some(x_prime).into_iter()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
};
|
||||
#+END_SRC
|
||||
|
||||
* To-do
|
||||
- Examine overhead introduced by nesting closures instead of using naked loops.
|
||||
- Benchmark.
|
||||
- ?Figure out how to avoid moving values we don't have to.
|
||||
- ?Figure out how to avoid creating some new iterators unnecessarily.
|
||||
|
||||
* Copyright
|
||||
|
||||
Copyright 2015, Donald S. Black.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
417
src/lib.rs
417
src/lib.rs
@@ -1,7 +1,262 @@
|
||||
#![feature(test)]
|
||||
// -*- mode: rust; rust-indent-offset: 4; -*-
|
||||
|
||||
//! Provides a macro that generates an iterable sequence via for comprehensions
|
||||
//! (AKA list comprehensions).
|
||||
//!
|
||||
//! If you're familiar with [Python's list
|
||||
//! comprehensions](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions)
|
||||
//! or [Scala's for
|
||||
//! comprehensions](https://docs.scala-lang.org/tour/for-comprehensions.html),
|
||||
//! this should be familiar. The use of the `yield` keyword is also similar to
|
||||
//! that of the recent [generator syntax
|
||||
//! RFC](https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md).
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Sequences are built out of `for` statements, which may be nested.
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use(iterate)] extern crate r4;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! 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.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! #[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.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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](macro.iterate.html) for a full breakdown of expressions it
|
||||
//! recognizes. In brief, they are:
|
||||
//!
|
||||
//! * `for x in xs`: introduces a new scope that iterates over `xs`, binding the pattern
|
||||
//! `x` to each of its values.
|
||||
//! * `if cond`: short-circuits subsequent expressions if `cond` is not `true`.
|
||||
//! * `if let pattern = expr`: introduces a new scope that binds `pattern` to `expr`,
|
||||
//! short-circuiting subsequent expressions if `pattern` does not match `expr`.
|
||||
//! * `let a = b`: introduces a new scope that binds `a` (which may be a pattern) to `b`.
|
||||
//! * `yield r`: emits the value of the expression `r`.
|
||||
//!
|
||||
//! # Example expansion
|
||||
//!
|
||||
//! This macro invocation:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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];
|
||||
//! # let v: Vec<i32> = items.collect();
|
||||
//! # assert_eq!(v, vec![5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15]);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! is expanded like so:
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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
|
||||
//! })
|
||||
//! })
|
||||
//! };
|
||||
//! # let v: Vec<i32> = items.collect();
|
||||
//! # assert_eq!(v, vec![5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15]);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # 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:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! 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])
|
||||
//! }
|
||||
//! # fn main() {
|
||||
//! # let v: Vec<u32> = generate_values().collect();
|
||||
//! # assert_eq!(v, vec![2, 4, 6, 8]);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! 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.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use(iterate)] extern crate r4;
|
||||
//! # fn main() {
|
||||
//! 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]);
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::iter::Iterator;
|
||||
|
||||
// Flat-maps an Option<I> where I: Iterator into any underlying Iterator.
|
||||
/// 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
|
||||
/// `iterate!` macro.
|
||||
///
|
||||
/// This is useful because both branches of an `if` expression must have the
|
||||
/// same type. In other words, the following is not valid:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use std::iter;
|
||||
/// # fn main() {
|
||||
/// let a = 1;
|
||||
/// let b = 2;
|
||||
/// let k =
|
||||
/// if a + b > b + b {
|
||||
/// // Type is std::vec::IntoIter<i32>.
|
||||
/// vec![0i32, 1, 2, 3, 4].into_iter() // Type mismatch.
|
||||
/// } else {
|
||||
/// // Type is std::iter::Empty.
|
||||
/// iter::empty() // Type mismatch.
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// We can get around this limitation by ensuring that both arms of an `if`
|
||||
/// expression resolve to a `FlatIter<I>`:
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate r4;
|
||||
/// # fn main() {
|
||||
/// let a = 1;
|
||||
/// let b = 2;
|
||||
/// let k =
|
||||
/// if a + b > b + b {
|
||||
/// // Type is r4::FlatIter<std::vec::IntoIter<i32>>.
|
||||
/// r4::FlatIter::new(Some(vec![0, 1, 2, 3, 4].into_iter()))
|
||||
/// } else {
|
||||
/// // Type is compatible with r4::FlatIter<std::vec::IntoIter<i32>>.
|
||||
/// r4::FlatIter::new(None)
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct FlatIter<I: Iterator> {
|
||||
wrapped: Option<I>,
|
||||
}
|
||||
@@ -30,26 +285,43 @@ impl<I: Iterator> Iterator for FlatIter<I> {
|
||||
}
|
||||
}
|
||||
|
||||
// Main entrypoint for iteration via for comprehensions. Accepts expressions
|
||||
// like:
|
||||
//
|
||||
// (for x in xs; (if a;|let b = c;)*)+ yield r
|
||||
//
|
||||
// Expressions are compiled into nested flat_map operations via closures that
|
||||
// move out of the enclosing environment. See package-level documentation for
|
||||
// details.
|
||||
/// Produces an iterator over values `yield`ed in a sequence of
|
||||
/// semicolon-separated expressions.
|
||||
///
|
||||
/// Expressions are compiled into nested flat_map operations via closures that
|
||||
/// move out of the enclosing environment. See [top-level
|
||||
/// documentation](./#lifetimes-and-moved-values) for details.
|
||||
///
|
||||
/// Implementation note: adjacent wildcard matches aren't allowed, so each
|
||||
/// recursive rule has multiple cases, wherein wildcard matches are separated by
|
||||
/// macro keywords.
|
||||
#[macro_export]
|
||||
macro_rules! iterate {
|
||||
// Implementation note: adjacent wildcard matches aren't allowed, so each
|
||||
// recursive rule has three cases, wherein the wildcards matches are
|
||||
// separated by macro keywords.
|
||||
|
||||
// yield
|
||||
(yield $r:expr) => (Some($r).into_iter());
|
||||
(yield $r:expr) => (::std::iter::once($r));
|
||||
// chained yield
|
||||
(yield $r:expr; for $($rest:tt)+) =>
|
||||
(iterate![yield $r].chain(iterate![for $($rest)+]));
|
||||
(yield $r:expr; if $($rest:tt)+) =>
|
||||
(iterate![yield $r].chain(iterate![if $($rest)+]));
|
||||
(yield $r:expr; let $($rest:tt)+) =>
|
||||
(iterate![yield $r].chain(iterate![let $($rest)+]));
|
||||
(yield $r:expr; yield $($rest:tt)+) =>
|
||||
(iterate![yield $r].chain(iterate![yield $($rest)+]));
|
||||
// for
|
||||
(for $x:ident in $xs:expr; $($rest:tt)*) =>
|
||||
(for $x:pat in $xs:expr; $($rest:tt)*) =>
|
||||
($xs.flat_map(move |$x| { iterate![$($rest)*] }));
|
||||
// if let
|
||||
(if let $x:pat = $e:expr; for $($rest:tt)*) =>
|
||||
($crate::FlatIter::new(if let $x = $e { Some(iterate![for $($rest)*]) } else { None }));
|
||||
(if let $x:pat = $e:expr; if $($rest:tt)*) =>
|
||||
($crate::FlatIter::new(if let $x = $e { Some(iterate![if $($rest)*]) } else { None }));
|
||||
(if let $x:pat = $e:expr; let $($rest:tt)*) =>
|
||||
($crate::FlatIter::new(if let $x = $e { Some(iterate![let $($rest)*]) } else { None }));
|
||||
(if let $x:pat = $e:expr; yield $($rest:tt)*) =>
|
||||
($crate::FlatIter::new(if let $x = $e { Some(iterate![yield $($rest)*]) } else { None }));
|
||||
// if if => if
|
||||
// This cuts down on the degree of FlatIter nesting when there are many if guards in a row.
|
||||
(if $a:expr; if $b:expr; $(if $c:expr;)* for $($rest:tt)*) =>
|
||||
(iterate![if $a && $b $(&& $c)*; for $($rest)*]);
|
||||
(if $a:expr; if $b:expr; $(if $c:expr;)* let $($rest:tt)*) =>
|
||||
@@ -74,7 +346,6 @@ macro_rules! iterate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate test;
|
||||
use std::fmt::Display;
|
||||
|
||||
fn check_match<N: PartialEq + Display, I: Iterator<Item=N>, J: Iterator<Item=N>>(mut i: I, mut j: J) {
|
||||
@@ -249,4 +520,116 @@ mod tests {
|
||||
for x in ys.iter();
|
||||
yield *x]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_just_multiple_yields() {
|
||||
check_match(vec![0, 1, 2, 3, 4, 5].into_iter(),
|
||||
iterate![yield 0;
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
yield 4;
|
||||
yield 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_yields_liked_nested_loops() {
|
||||
check_match(vec![0, 5, 6, 7, 8, 9,
|
||||
1, 5, 6, 7, 8, 9,
|
||||
2, 5, 6, 7, 8, 9,
|
||||
3, 5, 6, 7, 8, 9,
|
||||
4, 5, 6, 7, 8, 9].into_iter(),
|
||||
iterate![for x in 0..5;
|
||||
yield x;
|
||||
for y in 5..10;
|
||||
yield y]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_yield_with_filter_1() {
|
||||
check_match(vec![0, 5, 6, 7, 8, 9,
|
||||
1,
|
||||
2, 5, 6, 7, 8, 9,
|
||||
3,
|
||||
4, 5, 6, 7, 8, 9].into_iter(),
|
||||
iterate![for x in 0..5;
|
||||
yield x;
|
||||
if x % 2 == 0;
|
||||
for y in 5..10;
|
||||
yield y]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_yield_with_filter_2() {
|
||||
check_match(vec![0, 5, 6, 7, 8, 9,
|
||||
2, 5, 6, 7, 8, 9,
|
||||
4, 5, 6, 7, 8, 9].into_iter(),
|
||||
iterate![for x in 0..5;
|
||||
if x % 2 == 0;
|
||||
yield x;
|
||||
for y in 5..10;
|
||||
yield y]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_if_let() {
|
||||
check_match(vec![0, 1, 2, 3].into_iter(),
|
||||
iterate![for x in 0..4;
|
||||
if let Some(b) = Some(x);
|
||||
yield b]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_let_filter() {
|
||||
check_match(vec![].into_iter(),
|
||||
iterate![for x in 0..4;
|
||||
let c = if x > 5 { Some(x) } else { None };
|
||||
if let Some(b) = c;
|
||||
yield b]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_let_if() {
|
||||
check_match(vec![0, 2].into_iter(),
|
||||
iterate![for x in 0..4;
|
||||
if let Some(b) = Some(x);
|
||||
if b % 2 == 0;
|
||||
yield b]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_let_let() {
|
||||
check_match(vec![0, 2, 4, 6].into_iter(),
|
||||
iterate![for x in 0..4;
|
||||
if let Some(b) = Some(x);
|
||||
let c = b * 2;
|
||||
yield c]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_let_for() {
|
||||
check_match(vec![0,
|
||||
1, 2, 3,
|
||||
1,
|
||||
2, 3, 4,
|
||||
2,
|
||||
3, 4, 5].into_iter(),
|
||||
iterate![for x in 0..3;
|
||||
yield x;
|
||||
if let Some(b) = Some(x);
|
||||
for y in (b + 1)..(b + 4);
|
||||
yield y]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_pattern_in_for() {
|
||||
struct Item {
|
||||
x: u32,
|
||||
y: u32,
|
||||
};
|
||||
let items = (0..5).map(|x| Item { x: x, y: x * 2, });
|
||||
check_match(vec![0, 0, 1, 2, 2, 4, 3, 6, 4, 8].into_iter(),
|
||||
iterate![for Item { x, y, } in items;
|
||||
yield x; yield y]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user