Rust "combine" iterators? [duplicate] - rust

Is there a natural way in Rust to iterate over the "product" of several ranges or iterators?
This comes up when you're iterating over a multidimensional array, or perhaps some state space. For instance, I want to consider all possible values of a boolean tuple with 5 elements. Nesting 5 for loops is a bit unwieldy.

Here is a macro that does the job:
macro_rules! product {
($first:ident, $($next:ident),*) => (
$first.iter() $(
.flat_map(|e| std::iter::repeat(e)
.zip($next.iter()))
)*
);
}
fn main() {
let a = ['A', 'B', 'C'];
let b = [1, 4];
let c = [true, false];
let d = ['x', 'y'];
for (((a, b), c), d) in product![a, b, c, d] {
println!("{} {} {} {}", a, b, c, d);
}
}
Output:
A 1 true x
A 1 true y
A 1 false x
A 1 false y
A 4 true x
A 4 true y
etc...
Playpen example
The macro expands to the following
a.iter()
.flat_map(|e| std::iter::repeat(e).zip(b.iter()))
.flat_map(|e| std::iter::repeat(e).zip(c.iter()))
.flat_map(|e| std::iter::repeat(e).zip(d.iter()))
flat_map(|e| ... ) combines a sequence of iterators into an iterator. The e is an element yielded by an iterator.
std::iter::repeat(e) creates an iterator that repeats e.
.zip( ... ) iterates over two iterators simultaneously, yielding the elements of both as a pair.
Macros are a bit longer to explain, so it's best to read the macro chapter in the book

The itertools crate has a very ergonomic macro (iproduct!) for iterating over the product of iterators. Here is an example:
pub fn main() {
let a = ['A', 'B', 'C'];
let b = [1, 4];
let c = [true, false];
let d = ['x', 'y'];
for (a, b, c, d) in itertools::iproduct!(&a, &b, &c, &d) {
println!("{} {} {} {}", a, b, c, d);
}
}

Related

In rust, why does s[0..] returns a type of `str`?

In my understanding, s[0..] calls the method ops::Index::index. I try to call s.index(0..), but find that it returns a type of &str. The code is as follows
let a = String::from("hello world");
let b = &a;
let c = b.index(1..);
println!("{}", c);
let d = b[1..];
println!("{}", d);
This code will display an error, and indicate that the variable d is of type str.
What do I understand wrong? and how to understand the [] and index() in rust?
In the documentation of std::ops::Index, we find:
container[index] is actually syntactic sugar for *container.index(index)
and
This allows nice things such as let value = v[index] if the type of value implements Copy.
In your example, c has type &str as you expect, but d would be the dereference (*) of such an &str; this would lead to str which is rejected by the compiler (not sized).
In order to obtain a &str in d, you could write:
let d = &b[1..]; // & was missing
which is really explicit because it clearly states: «I want to refer to something which stands inside b».
Here is another example of the implicit dereference (*) in a different context:
let mut arr = [1, 2, 3, 4];
let n = arr[1]; // not *arr[1]
arr[2] += n; // not *arr[2]
println!("{:?}", arr); // [1, 2, 5, 4]

Idiomatic way to optionally add one item to an iterator? [duplicate]

Let's say I have:
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
Then I have boolean conditions i, j and k. I want to generate an iterator that conditionally chains it, jt and kt together based on the values of i, j and k. Can I do this with just the built-in Rust Iterator functionality?
You can make Option into an iterator.
let it = i.then_some([1, 2, 3]).into_iter().flatten();
let jt = j.then_some([4, 5, 6]).into_iter().flatten();
let kt = k.then_some([7, 8, 9]).into_iter().flatten();
let iter = it.chain(jt).chain(kt);
If the condition is false, then condition.then_some(...) will return None, making an empty iterator. Otherwise a Some(...) is returned. into_iter().flatten() will transform Option<impl IntoIterator<Item=T>> to impl Iterator<Item=T>.
You're going to run into a slight issue if you want to use bare iterators:
If you write the following:
let iter = [1, 2, 3].into_iter();
let iter = if some_condition {
iter.chain([4, 5, 6])
} else {
iter
}
You'll get an error which boils down to this:
= note: expected struct `std::iter::Chain<std::array::IntoIter<_, _>, std::array::IntoIter<{integer}, 3>>`
found struct `std::array::IntoIter<_, _>`
iter has type IntoIter, but iter.chain() has type Chain<IntoIter, ...>
To get around this, you have a few options:
you can use a trait object, which behaves a bit like an interface from languages like Java, but loses some performance:
let iter = [1, 2, 3].into_iter();
let mut iter: Box<dyn Iterator<Item = i32>> = Box::new(iter);
if some_condition {
iter = Box::new(iter.chain([4, 5, 6]));
}
or, probably a better solution, if you can sacrifice laziness, is to just use a Vec:
// save heap allocations by pre-allocating the whole vec
let len = if some_condition { 6 } else { 3 };
let mut items = Vec::with_capacity(len);
items.extend([1, 2, 3]);
if some_condition {
items.extend([4, 5, 6]);
}
This is a good use for the either crate. Either implements Iterator when both the left and right sides also implement Iterator, so it can be easily used to chain iterators together.
Given any three iterators it, jt, and kt that iterate over the same Item, with accompanying booleans i, j, and k, you can write a function that chains them together like this:
use either::Either;
use std::iter;
fn chain<'a, I, J, K, Item>(
it: I,
jt: J,
kt: K,
i: bool,
j: bool,
k: bool,
) -> iter::Chain<
iter::Chain<Either<I, iter::Empty<Item>>, Either<J, iter::Empty<Item>>>,
Either<K, iter::Empty<Item>>,
>
where
I: Iterator<Item = Item>,
J: Iterator<Item = Item>,
K: Iterator<Item = Item>,
{
let iter = if i {
Either::Left(it)
} else {
Either::Right(iter::empty())
};
let iter = iter.chain(if j {
Either::Left(jt)
} else {
Either::Right(iter::empty())
});
let iter = iter.chain(if k {
Either::Left(kt)
} else {
Either::Right(iter::empty())
});
iter
}
Calling this function will result in an iterator conditional on the input. For example, calling
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
chain(it, jt, kt, true, false, true).collect::<Vec<_>>();
gives
[1, 2, 3, 7, 8, 9]
as expected.
You can try it using this playground.

How do I get the cartesian product of 2 vectors by using Iterator?

I have 2 Vecs:
let x = vec!['1', '2', '3'];
let y = vec!['a', 'b', 'c'];
Now I want to use iterator to make a new vec like this ['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']. How can I do?
Easiest way would be to use the cartesian product macro available in the itertools crate
use itertools::iproduct; // 0.10.1
fn main() {
let x = vec!['1', '2', '3'];
let y = vec!['a', 'b', 'c'];
let product: Vec<String> = iproduct!(x, y)
.map(|(a, b)| format!("{}{}", a, b))
.collect();
println!("{:?}", product);
}
Playground
Here is how to do it with vanilla Rust iterators:
fn main() {
let x = vec!['1', '2', '3'];
let y = vec!['a', 'b', 'c'];
let product: Vec<String> = x
.iter()
.map(|&item_x| y
.iter()
.map(move |&item_y| [item_x, item_y]
.iter()
.collect()
)
)
.flatten()
.collect();
println!("{:?}", product);
}
Explanation
The easiest way to construct a String from two chars is to collect iterator over the chars:
let string: String = [item_x, item_y].iter().collect();
For each item in x we iterate over y and construct such string.
x.iter().map(|&item_x| y.iter.map(move |&item_y| ...));
We use pattern matching to get value in the map closure rather then references. Because of that and the fact that the char has Copy trait, we can move item_x into inner closure, resolving any lifetime issues.
As the result of the code above we get an iterator over iterators over Strings. To flatten that iterator, we use flatten method (who would think?). Then we collect the flat iterator into the resulting Vec.
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bf2987ed96303a0db0f629884492011e
The existing answers make sense if your goal is to get the Cartesian product of two iterators. If you've got vectors or slices already though (like in the original question) you can do a little better:
fn main() {
let x = vec!['1', '2', '3'];
let y = vec!['a', 'b', 'c'];
let result: Vec<String> = product(&x, &y)
.map(|(a, b)| format!("{}{}", a, b))
.collect();
println!("{:?}", result)
}
fn product<'a: 'c, 'b: 'c, 'c, T>(
xs: &'a [T],
ys: &'b [T],
) -> impl Iterator<Item = (&'a T, &'b T)> + 'c {
xs.iter().flat_map(move |x| std::iter::repeat(x).zip(ys))
}
Playground
Any iterator based solution will necessarily require storing the full contents of the iterator somewhere, but if you already have the data in a vector or array you can use the known size to only store the indices instead.

How to iterate over two elements in a collection stepping by one using Iterator?

Suppose we have a vector:
let a = vec![1, 2, 3];
What is the best and shortest way to iterate over the elements so, that in the first iteration I receive a tuple of (1, 2), and in the next iteration - (2, 3), until there are no elements, so without producing the (3, None) or anything like that? It seems that a.chunks(2) is a bit different, it steps by two, while I need to step by one over every two consecutive elements in a collection.
There are multiple ways to do so:
Using the standard library:
.zip
let result = a.iter()
.zip(a.iter().skip(1))
.inspect(|(a, b)| println!("a: {}, b: {}", a, b)
.collect::<Vec<_>>();
.windows
let result = a.windows(2)
.inspect(|w| println!("a: {}, b: {}", w[0], w[1]))
.collect::<Vec<_>>();
Worth noting, in case this matters, is that windows iterates over subslices, so unlike the above method, collect() in this case will not give you a Vec of tuples.
Using the Itertools crate:
.tuple_windows
use itertools::Itertools;
let result = a.iter()
.tuple_windows()
.inspect(|(a, b)| println!("a: {}, b: {}", a, b))
.collect::<Vec<_>>();
All of the above methods will print the following:
a: 1, b: 2
a: 2, b: 3
As a bonus, Itertools as of recent also has a .circular_tuple_windows(), which performs an extra iteration by including the last (3) and first (1) element:
use itertools::Itertools;
let result = a.iter()
.circular_tuple_windows()
.inspect(|(a, b)| println!("a: {}, b: {}", a, b))
.collect::<Vec<_>>();
a: 1, b: 2
a: 2, b: 3
a: 3, b: 1
With the nightly-only (as of early 2021) array_windows feature, you can iterate over length-2 array references:
for [a, b] in arg.array_windows() {
println!("a: {}, b: {}", a, b);
}
Playground
Note the use of a slice pattern [a, b], not a tuple pattern (a, b).
On stable you can achieve the same thing with windows and try_into, but it's not as clean:
use std::convert::TryInto;
for s in arg.windows(2) {
let [a, b]: [i32; 2] = s.try_into().unwrap();
println!("a: {}, b: {}", a, b);
}
You can also use chunks(). Now the elements will not overlap.
fn main() {
let vec = vec![1, 2, 4, 5, 8, 9];
let new_thing = vec.chunks(2).map(|x| println!("{} and {}", x[0],
x[1])).collect::<Vec<_>>();
}
1 and 2
4 and 5
8 and 9

Assign new value via tuple destructuring [duplicate]

I'd like to destructure a tuple and assign part of the result to a new variable and assign another part of the result to an existing.
The following code illustrates the intent (it's a dumb example which results in an infinite loop printing [0]):
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let (head, list) = list.split_at(1);
// An obvious workaround here is to introduce a new variable in the above
// let statement, and then just assign it to list.
println!("{:?}", head);
}
}
This code creates a new variable list instead of reassigning it.
If I change the code to the following (to avoid the let that introduces the new list variable), it doesn't compile:
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let head;
(head, list) = list.split_at(1);
println!("{:?}", head);
}
}
Compilation error:
error[E0070]: invalid left-hand side of assignment
--> src/main.rs:5:22
|
5 | (head, list) = list.split_at(1);
| ------------ ^
| |
| cannot assign to this expression
|
Is there a way to do this, or can destructuring only be used in let, match, and for statements?
No.
Destructuring is something you can only do with patterns; the left-hand side of an assignment is not a pattern, hence you can't destructure-and-assign.
See proto-RFC 372 (Destructuring assignment) which discusses the possibility of adding this feature.
Yes.
The Rust team has published a new version of Rust 1.59.0 in Feb. 24, 2022, you can now use tuple, slice, and struct patterns as the left-hand side of an assignment.
Announcing Rust 1.59.0
Destructuring assignments
You can now use tuple, slice, and struct patterns as the left-hand side of an assignment.
let (a, b, c, d, e);
(a, b) = (1, 2); [c, .., d, _] = [1, 2, 3, 4, 5]; Struct { e, .. } =
Struct { e: 5, f: 3 };
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
This makes assignment more consistent with let bindings, which have
long supported the same thing. Note that destructuring assignments
with operators such as += are not allowed.
Before 1.59.0, you can only destructure it in Nightly version with #![feature(destructuring_assignment)].
Now you can do this trick in stable version and remove the feature line.
See more details from rust-lang/rust/issues/71126 and rust-lang/rust/pull/90521.
Nightly Rust has feature(destructuring_assignment), which allows your original attempt to compile as-is:
#![feature(destructuring_assignment)]
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let head;
(head, list) = list.split_at(1);
println!("{:?}", head);
}
}
[0]
[1]
[2]
[3]
However, I'd solve this using stable features like slice pattern matching, which avoids the need for the double check in split_at and is_empty:
fn main() {
let mut list = &[0, 1, 2, 3][..];
while let [head, rest # ..] = list {
println!("{:?}", head);
list = rest;
}
}
See also:
How to swap two variables?

Resources