I have a grid: Vec<Vec<Object>> and a pair of x/y indices. I want to find all the elements surrounding the one indexed.
Unfortunately, I can't simply loop over the elements because that ends up borrowing the Vec twice and the borrow checker screams at me:
let mut cells = Vec::with_capacity(8);
for cx in xstart..xend {
for cy in ystart..yend {
if cx != x || cy != y {
cells.push(&mut squares[cy as usize][cx as usize]);
}
}
}
cells.into_iter()
My best attempt at changing this into an iterator chain has also failed spectacularly:
let xstart = if x == 0 { x } else { x - 1 };
let xlen = if x + 2 > squares[0].len() { x + 1 } else { 3 };
let ystart = if y == 0 { y } else { y - 1 };
let ylen = if y + 2 > squares.len() { y + 1 } else { 3 };
let xrel = x - xstart;
let yrel = y - ystart;
squares.iter().enumerate()
.skip(ystart).take(ylen).flat_map(|(i, ref row)|
row.iter().enumerate()
.skip(xstart).take(xlen).filter(|&(j, &c)| i != yrel || j != xrel))
Does anyone know how I can do this?
Personally, I am not sure I would be comfortable working with an iterator when the relative positions of the elements can be important. Instead, I would seek to create a "view" of those elements.
The gist can be found here, but the idea is simple so here are the core structures.
#[derive(Debug)]
struct NeighbourhoodRow<'a, T>
where T: 'a
{
pub left : Option<&'a mut T>,
pub center : Option<&'a mut T>,
pub right : Option<&'a mut T>,
}
#[derive(Debug)]
struct Neighbourhood<'a, T>
where T: 'a
{
pub top : NeighbourhoodRow<'a, T>,
pub center : NeighbourhoodRow<'a, T>,
pub bottom : NeighbourhoodRow<'a, T>,
}
To build them, I use a healthy dose of split_at_mut:
fn take_centered_trio<'a, T>(row: &'a mut [T], x: usize) ->
(Option<&'a mut T>, Option<&'a mut T>, Option<&'a mut T>)
{
fn extract<'a, T>(row: &'a mut [T], x: usize) -> (Option<&'a mut T>, &'a mut [T]) {
if x+1 > row.len() {
(None, row)
} else {
let (h, t) = row.split_at_mut(x+1);
(Some(&mut h[x]), t)
}
}
let (prev, row) = if x > 0 { extract(row, x-1) } else { (None, row) };
let (elem, row) = extract(row, 0);
let (next, _ ) = extract(row, 0);
(prev, elem, next)
}
and the rest is just some uninteresting constructors.
Of course, you can then build some kind of iterator over those.
In the end I made a custom iterator with the help of the guys in #rust
I've typed my struct out to give you the actual code. As pointed out by the guys in #rust you cannot return &mut safely from an iterator without using a different iterator that uses unsafe anyway, and given that the math here is simple enough to ensure it doesn't go wrong an unsafe was the way to go.
type FieldSquare = u8;
use std::iter::Iterator;
pub struct SurroundingSquaresIter<'a> {
squares: &'a mut Vec<Vec<FieldSquare>>,
center_x: usize,
center_y: usize,
current_x: usize,
current_y: usize,
}
pub trait HasSurroundedSquares<'a> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a>;
}
impl<'a> HasSurroundedSquares<'a> for Vec<Vec<FieldSquare>> {
fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a> {
SurroundingSquaresIter {
squares: self,
center_x: x,
center_y: y,
current_x: if x == 0 { x } else { x - 1 },
current_y: if y == 0 { y } else { y - 1 },
}
}
}
impl<'a> Iterator for SurroundingSquaresIter<'a> {
type Item = &'a mut FieldSquare;
fn next(&mut self) -> Option<&'a mut FieldSquare> {
if self.current_y + 1 > self.squares.len() || self.current_y > self.center_y + 1 {
return None;
}
let ret_x = self.current_x;
let ret_y = self.current_y;
if self.current_x < self.center_x + 1 && self.current_x + 1 < self.squares[self.current_y].len() {
self.current_x += 1;
}
else {
self.current_x = if self.center_x == 0 { self.center_x } else { self.center_x - 1 };
self.current_y += 1;
}
if ret_x == self.center_x && ret_y == self.center_y {
return self.next();
}
Some(unsafe { &mut *(&mut self.squares[ret_y][ret_x] as *mut _) })
}
}
You want to get mutable references to all surrounding elements, right? I don't think this is possible to do it directly. The problem is, Rust cannot statically prove that you want mutable references to different cells. If it ignored this, then, for example, you could make a slight mistake in indexing and get two mutable references to the same data, which is something Rust guarantees to prevent. Hence it disallows this.
On the language level this is caused by IndexMut trait. You can see how its only method's self parameter lifetime is tied to the result lifetime:
fn index_mut(&'a mut self, index: Idx) -> &'a mut Self::Output;
This means that if this method is called (implicitly through an indexing operation) then the whole object will be borrowed mutably until the resulting reference goes out of scope. This prevents calling &mut a[i] multiple times.
The most simple and safest way to fix this would be to refactor your code in a "double buffering" manner - you have two instances of the field and copy data between each other on the each step. Alternatively, you can create a temporary field on each step and replace the main one with it after all computations but it is probably less efficient than swapping two fields.
Another way to solve this would be, naturally, using raw *mut pointers. This is unsafe and should only be used directly as the last resort. You can use unsafety, however, to implement a safe abstraction, something like
fn index_multiple_mut<'a, T>(input: &'a mut [Vec<T>], indices: &[(usize, usize)]) -> Vec<&'a mut T>
where you first check that all indices are different and then use unsafe with some pointer casts (with transmute, probably) to create the resulting vector.
A third possible way would be to use split_at_mut() method in some clever way, but I'm not that sure that it is possible, and if it is, it is likely not very convenient.
Related
I have a custom iterator and I would like to optionally call .skip(...) in the custom .next() method. However, I get a type error because Skip != Iterator.
Sample code is as follows:
struct CrossingIter<'a, T> {
index: usize,
iter: std::slice::Iter<'a, T>,
}
impl<'a, T: Float> Iterator for CrossingIter<'a, T> {
type Item = (usize, T);
fn next(&mut self) -> Option<(usize, T)> {
let iter = (&mut self.iter).enumerate();
let iter = if self.index == 0 {
self.index += 3;
iter.skip(3)
} else {
iter
}
// lots of code here working with the new iterator
iter.next()
}
}
The issue is that after calling .skip(3), the type of iter has changed. One solution would be to duplicate the // lots of code ... in each branch of the if statement, but I'd rather not.
My question is: Is there a way to conditionally apply skip(...) to an iterator and continue working with it without duplicating a bunch of code?
skip is designed to construct a new iterator, which is very useful in situations where you want your code to remain, at least on the surface, immutable. However, in your case, you want to advance the existing iterator while still leaving it valid.
There is advance_by which does what you want, but it's Nightly so it won't run on Stable Rust.
if self.index == 0 {
self.index += 3;
self.iter.advance_by(3);
}
We can abuse nth to get what we want, but it's not very idiomatic.
if self.index == 0 {
self.index += 3;
self.iter.nth(2);
}
If I saw that code in production, I'd be quite puzzled.
The simplest and not terribly satisfying answer is to just reimplement advance_by as a helper function. The source is available and pretty easy to adapt
fn my_advance_by(iter: &mut impl Iterator, n: usize) -> Result<(), usize> {
for i in 0..n {
iter.next().ok_or(i)?;
}
Ok(())
}
All this being said, if your use case is actually just to skip the first three elements, all you need is to start with the skip call and assume your iterator is always Skip
struct CrossingIter<'a, T> {
index: usize,
iter: std::iter::Skip<std::slice::Iter<'a, T>>,
}
I think #Silvio's answer is a better perspective.
You may call skip(0) instead of the iter itself in else branch...
And the return value of the iterator generated by enumerate doesn't match your definition: fn next(&mut self) -> Option<(usize, T)>. You need to map it.
Here is a working example:
use num::Float;
struct CrossingIter<'a, T> {
index: usize,
iter: std::slice::Iter<'a, T>,
}
impl<'a, T: Float> Iterator for CrossingIter<'a, T> {
type Item = (usize, T);
fn next(&mut self) -> Option<(usize, T)> {
let iter = (&mut self.iter).enumerate();
let mut iter = if self.index == 0 {
self.index += 3;
iter.skip(3)
} else {
iter.skip(0)
};
// lots of code here working with the new iterator
iter.next().map(|(i, &v)| (i, v))
}
}
I have some object that I want to split into two parts via a mutable borrow, then combine those back together into the original object when the split references go out of scope.
The simplified example below is for a Count struct that holds a single i32, which we want to split into two &mut i32s, who are both incorporated back into the original Count when the two mutable references go out of scope.
The approach I am taking below is to use an intermediate object CountSplit which holds a mutable reference to the original Count object and has the Drop trait implemented to do the re-combination logic.
This approach feels kludgy. In particular, this is awkward:
let mut ms = c.make_split();
let (x, y) = ms.split();
Doing this in one line like let (x, y) = c.make_split().split(); is not allowed because the intermediate object must have a longer lifetime. Ideally I would be able to do something like let (x, y) = c.magic_split(); and avoid exposing the intermediate object altogether.
Is there a way to do this which doesn't require doing two let's every time, or some other way to tackle this pattern that would be more idiomatic?
#[derive(Debug)]
struct Count {
val: i32,
}
trait MakeSplit<'a> {
type S: Split<'a>;
fn make_split(&'a mut self) -> Self::S;
}
impl<'a> MakeSplit<'a> for Count {
type S = CountSplit<'a>;
fn make_split(&mut self) -> CountSplit {
CountSplit {
top: self,
second: 0,
}
}
}
struct CountSplit<'a> {
top: &'a mut Count,
second: i32,
}
trait Split<'a> {
fn split(&'a mut self) -> (&'a mut i32, &'a mut i32);
}
impl<'a, 'b> Split<'a> for CountSplit<'b> {
fn split(&mut self) -> (&mut i32, &mut i32) {
(&mut self.top.val, &mut self.second)
}
}
impl<'a> Drop for CountSplit<'a> {
fn drop(&mut self) {
println!("custom drop occurs here");
self.top.val += self.second;
}
}
fn main() {
let mut c = Count { val: 2 };
println!("{:?}", c); // Count { val: 2 }
{
let mut ms = c.make_split();
let (x, y) = ms.split();
println!("split: {} {}", x, y); // split: 2 0
// each of these lines correctly gives a compile-time error
// c.make_split(); // can't borrow c as mutable
// println!("{:?}", c); // or immutable
// ms.split(); // also can't borrow ms
*x += 100;
*y += 5000;
println!("split: {} {}", x, y); // split: 102 5000
} // custom drop occurs here
println!("{:?}", c); // Count { val: 5102 }
}
playground:
I don't think a reference to a temporary value like yours can be made to work in today's Rust.
If it's any help, if you specifically want to call a function with two &mut i32 parameters like you mentioned in the comments, e.g.
fn foo(a: &mut i32, b: &mut i32) {
*a += 1;
*b += 2;
println!("split: {} {}", a, b);
}
you can already do that with the same number of lines as you'd have if your chaining worked.
With the chaining, you'd call
let (x, y) = c.make_split().split();
foo(x, y);
And if you just leave out the conversion to a tuple, it looks like this:
let mut ms = c.make_split();
foo(&mut ms.top.val, &mut ms.second);
You can make it a little prettier by e.g. storing the mutable reference to val directly in CountSplit as first, so that it becomes foo(&mut ms.first, &mut ms.second);. If you want it to feel even more like a tuple, I think you can use DerefMut to be able to write foo(&mut ms.0, &mut ms.1);.
Alternatively, you can of course formulate this as a function taking a function
impl Count {
fn as_split<F: FnMut(&mut i32, &mut i32)>(&mut self, mut f: F) {
let mut second = 0;
f(&mut self.val, &mut second);
self.val += second;
}
}
and then just call
c.as_split(foo);
I am trying to write a program that will find the longest path in the graph (i.e. the greatest depth) for a directed graph which is always a rooted or multi-rooted tree.
The specs of the assignment require I use DFS and memoization, but multiple mutable references occur when performing the DFS. Is there any other way to do this?
I thought about using HashMaps instead of internal Graph fields, but it would just produce the same error on mutability of the HashMap. I've found several other questions on the Rust user forum and here, but none of them gives the advise on how to resolve this. Am I supposed to use "unsafe" code or some other strategy?
use std::io;
struct Node {
neighbours: Vec<usize>,
depth: usize,
visited: bool,
}
impl Node {
fn new() -> Node { Node { neighbours: Vec::new(), depth: 0, visited: false } }
fn add_neighbour(&mut self, node: usize) { self.neighbours.push(node); }
fn neighbourhood_size(&self) -> usize { self.neighbours.len() }
}
struct Graph {
nodes: Vec<Node>,
depth: usize,
}
impl Graph {
fn new() -> Graph { Graph { nodes: Vec::new(), depth: 0} }
fn nodes_number(&self) -> usize { self.nodes.len()}
fn add_node(&mut self) { self.nodes.push(Node::new()); }
fn node(&mut self, i: usize) -> &mut Node { &mut self.nodes[i] }
fn dfs(graph: &mut Graph, index: usize) {
if !graph.node(index).visited {
graph.node(index).visited = true;
}
match graph.node(index).neighbourhood_size() == 0 {
true => { graph.node(index).depth = 1; },
false => {
for &i in graph.node(index).neighbours.iter() {
// multiple mutable references
Graph::dfs(graph, i);
}
graph.node(index).depth =
1 + graph.node(index).
neighbours.iter().
map(|&x| graph.node(x).depth).
max().unwrap();
}
}
if graph.node(index).depth > graph.depth {
graph.depth = graph.node(index).depth;
}
}
}
fn main() {
let mut input_line = String::new();
io::stdin().read_line(&mut input_line);
let n = input_line.trim().parse::<usize>().unwrap();
// to avoid counting from 0 or excessive use of (-1)
let mut graph = Graph::new(); graph.add_node();
for _ in 0 .. n {
let mut input_line = String::new();
io::stdin().read_line(&mut input_line);
let separated = input_line.
split(" ").
collect::<Vec<_>>();
let u = separated[0].trim().parse::<usize>().unwrap();
let v = separated[1].trim().parse::<usize>().unwrap();
if graph.nodes_number() <= u { graph.add_node(); }
if graph.nodes_number() <= v { graph.add_node(); }
graph.node(u).add_neighbour(v);
}
let n = graph.nodes_number();
for i in 1 .. n {
if !graph.node(i).visited { Graph::dfs(&mut graph, i); }
}
println!("{}", graph.depth);
}
Instead of taking a copy of the vector before iterating over it, you could also iterate over the indices:
for ni in 0..graph.node(index).neighbours.len() {
let neighbour = graph.node(index).neighbours[ni];
Graph::dfs(graph, neighbour);
}
The neighbours vector gets still borrowed for performing the iteration, but not for the whole course of the iteration:
graph.node(index).neighbours.len(): once at the beginning of the iteration for getting the length
let neighbour = graph.node(index).neighbours[ni];: in each iteration step for getting the neighbour at the current index
Like the copy approach, this solution is based on the constraint that the neighbours vector you are iterating over will not be changed by the call to dfs.
You can solve the remaining issues regarding multiple references in your code by providing immutable access to the graph nodes:
fn node_mut(&mut self, i: usize) -> &mut Node {
&mut self.nodes[i]
}
fn node(&self, i: usize) -> &Node {
&self.nodes[i]
}
Only make use of the mutable access via node_mut where necessary. For example when adding a neighbour: graph.node_mut(u).add_neighbour(v);
You are modifying your graph structure while iterating through a vector contained within it. The compiler has no way of verifying that you do not add or remove from the vector during the iteration, which would invalidate the iterator. This is the intuitive reason for the error.
The easiest way to avoid this is to take a copy of the vector before iterating over it, so the compiler can see that the iterator does not change. This is a little suboptimal but resolves the error for now. Another lifetime error is solved in a similar way (but without much cost) by copying the depth into a variable before doing a comparison.
use std::io;
use std::env;
struct Node {
neighbours: Vec<usize>,
depth: usize,
visited: bool,
}
impl Node {
fn new() -> Node {
Node {
neighbours: Vec::new(),
depth: 0,
visited: false,
}
}
fn add_neighbour(&mut self, node: usize) {
self.neighbours.push(node);
}
fn neighbourhood_size(&self) -> usize {
self.neighbours.len()
}
}
struct Graph {
nodes: Vec<Node>,
depth: usize,
}
impl Graph {
fn new() -> Graph {
Graph {
nodes: Vec::new(),
depth: 0,
}
}
fn nodes_number(&self) -> usize {
self.nodes.len()
}
fn add_node(&mut self) {
self.nodes.push(Node::new());
}
fn node(&mut self, i: usize) -> &mut Node {
&mut self.nodes[i]
}
fn dfs(graph: &mut Graph, index: usize) {
if !graph.node(index).visited {
graph.node(index).visited = true;
}
match graph.node(index).neighbourhood_size() == 0 {
true => {
graph.node(index).depth = 1;
}
false => {
let neighbours = graph.node(index).neighbours.clone();
for &i in neighbours.iter() {
// multiple mutable references
Graph::dfs(graph, i);
}
graph.node(index).depth = 1
+ neighbours
.iter()
.map(|&x| graph.node(x).depth)
.max()
.unwrap();
}
}
let depth = graph.node(index).depth;
if depth > graph.depth {
graph.depth = graph.node(index).depth;
}
}
}
fn main() {
env::set_var("RUST_BACKTRACE", "1");
let mut input_line = String::new();
io::stdin().read_line(&mut input_line);
let n = input_line.trim().parse::<usize>().unwrap();
// to avoid counting from 0 or excessive use of (-1)
let mut graph = Graph::new();
graph.add_node();
for _ in 0..n {
let mut input_line = String::new();
io::stdin().read_line(&mut input_line);
let separated = input_line.split(" ").collect::<Vec<_>>();
let u = separated[0].trim().parse::<usize>().unwrap();
let v = separated[1].trim().parse::<usize>().unwrap();
if graph.nodes_number() <= u {
graph.add_node();
}
if graph.nodes_number() <= v {
graph.add_node();
}
graph.node(u).add_neighbour(v);
}
let n = graph.nodes_number();
for i in 1..n {
if !graph.node(i).visited {
Graph::dfs(&mut graph, i);
}
}
println!("{}", graph.depth);
}
playground
If you were to modify your approach so that you did not mutate the structure during the search (i.e. you stored the visited data elsewhere), the code would work without this copy. This would also be more friendly to concurrent use.
I can do something like this:
fn func() -> (Vec<i32>, Vec<i32>) {
let mut u = vec![0;5];
let mut v = vec![0;5];
fn foo(u: &mut [i32], v: &mut [i32], i: usize, j: usize) {
for k in i+1..u.len() {
u[k] += 1;
bar(u, v, k, j);
}
}
fn bar(u: &mut [i32], v: &mut [i32], i: usize, j: usize) {
for k in j+1..v.len() {
v[k] += 1;
foo(u, v, i, k);
}
}
foo(&mut u, &mut v, 0, 0);
(u,v)
}
fn main() {
let (u,v) = func();
println!("{:?}", u);
println!("{:?}", v);
}
but I would prefer to do something like this:
fn func() -> (Vec<i32>, Vec<i32>) {
let mut u = vec![0;5];
let mut v = vec![0;5];
let foo = |i, j| {
for k in i+1..u.len() {
u[k] += 1;
bar(k, j);
}
};
let bar = |i, j| {
for k in j+1..v.len() {
v[k] += 1;
foo(i, k);
}
};
foo(0, 0);
(u,v)
}
fn main() {
let (u,v) = func();
println!("{:?}", u);
println!("{:?}", v);
}
The second example doesn't compile with the error: unresolved name bar.
In my task I can do it through one recursion, but it will not look clear.
Does anyone have any other suggestions?
I have a solution for mutually recursive closures, but it doesn't work with multiple mutable borrows, so I couldn't extend it to your example.
There is a way to use define mutually recursive closures, using an approach similar to how this answer does single recursion. You can put the closures together into a struct, where each of them takes a borrow of that struct as an extra argument.
fn func(n: u32) -> bool {
struct EvenOdd<'a> {
even: &'a Fn(u32, &EvenOdd<'a>) -> bool,
odd: &'a Fn(u32, &EvenOdd<'a>) -> bool
}
let evenodd = EvenOdd {
even: &|n, evenodd| {
if n == 0 {
true
} else {
(evenodd.odd)(n - 1, evenodd)
}
},
odd: &|n, evenodd| {
if n == 0 {
false
} else {
(evenodd.even)(n - 1, evenodd)
}
}
};
(evenodd.even)(n, &evenodd)
}
fn main() {
println!("{}", func(5));
println!("{}", func(6));
}
While defining mutually recursive closures works in some cases, as demonstrated in the answer by Alex Knauth, I don't think that's an approach you should usually take. It is kind of opaque, has some limitations pointed out in the other answer, and it also has a performance overhead since it uses trait objects and dynamic dispatch at runtime.
Closures in Rust can be thought of as functions with associated structs storing the data you closed over. So a more general solution is to define your own struct storing the data you want to close over, and define methods on that struct instead of closures. For this case, the code could look like this:
pub struct FooBar {
pub u: Vec<i32>,
pub v: Vec<i32>,
}
impl FooBar {
fn new(u: Vec<i32>, v: Vec<i32>) -> Self {
Self { u, v }
}
fn foo(&mut self, i: usize, j: usize) {
for k in i+1..self.u.len() {
self.u[k] += 1;
self.bar(k, j);
}
}
fn bar(&mut self, i: usize, j: usize) {
for k in j+1..self.v.len() {
self.v[k] += 1;
self.foo(i, k);
}
}
}
fn main() {
let mut x = FooBar::new(vec![0;5], vec![0;5]);
x.foo(0, 0);
println!("{:?}", x.u);
println!("{:?}", x.v);
}
(Playground)
While this can get slightly more verbose than closures, and requires a few more explicit type annotations, it's more flexible and easier to read, so I would generally prefer this approach.
How can I iterate over a range in Rust with a step other than 1? I'm coming from a C++ background so I'd like to do something like
for(auto i = 0; i <= n; i+=2) {
//...
}
In Rust I need to use the range function, and it doesn't seem like there is a third argument available for having a custom step. How can I accomplish this?
range_step_inclusive and range_step are long gone.
As of Rust 1.28, Iterator::step_by is stable:
fn main() {
for x in (1..10).step_by(2) {
println!("{}", x);
}
}
It seems to me that until the .step_by method is made stable, one can easily accomplish what you want with an Iterator (which is what Ranges really are anyway):
struct SimpleStepRange(isize, isize, isize); // start, end, and step
impl Iterator for SimpleStepRange {
type Item = isize;
#[inline]
fn next(&mut self) -> Option<isize> {
if self.0 < self.1 {
let v = self.0;
self.0 = v + self.2;
Some(v)
} else {
None
}
}
}
fn main() {
for i in SimpleStepRange(0, 10, 2) {
println!("{}", i);
}
}
If one needs to iterate multiple ranges of different types, the code can be made generic as follows:
use std::ops::Add;
struct StepRange<T>(T, T, T)
where for<'a> &'a T: Add<&'a T, Output = T>,
T: PartialOrd,
T: Clone;
impl<T> Iterator for StepRange<T>
where for<'a> &'a T: Add<&'a T, Output = T>,
T: PartialOrd,
T: Clone
{
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.0 < self.1 {
let v = self.0.clone();
self.0 = &v + &self.2;
Some(v)
} else {
None
}
}
}
fn main() {
for i in StepRange(0u64, 10u64, 2u64) {
println!("{}", i);
}
}
I'll leave it to you to eliminate the upper bounds check to create an open ended structure if an infinite loop is required...
Advantages of this approach is that is works with for sugaring and will continue to work even when unstable features become usable; also, unlike the de-sugared approach using the standard Ranges, it doesn't lose efficiency by multiple .next() calls. Disadvantages are that it takes a few lines of code to set up the iterator so may only be worth it for code that has a lot of loops.
If you are stepping by something predefined, and small like 2, you may wish to use the iterator to step manually. e.g.:
let mut iter = 1..10;
loop {
match iter.next() {
Some(x) => {
println!("{}", x);
},
None => break,
}
iter.next();
}
You could even use this to step by an arbitrary amount (although this is definitely getting longer and harder to digest):
let mut iter = 1..10;
let step = 4;
loop {
match iter.next() {
Some(x) => {
println!("{}", x);
},
None => break,
}
for _ in 0..step-1 {
iter.next();
}
}
Use the num crate with range_step
You'd write your C++ code:
for (auto i = 0; i <= n; i += 2) {
//...
}
...in Rust like so:
let mut i = 0;
while i <= n {
// ...
i += 2;
}
I think the Rust version is more readable too.