How to use multiple variables in Rust's for loop? - rust

In the C family of languages, I can do this on one line:
for(int i = lo, int j = mid+1; i <= mid && j <= hi; i++, j++){
...
}
But in Rust... I can only write it like this:
for i in lo..mid+1 {
let mut j = mid+1;
if j <= hi {
break;
}
...
j += 1;
}
Is there's a more efficient way to implement this?
Using an iterator works for above, but using an iterator makes some occasions like using arithmetic troublesome, such as
for (int i = 0; i < n; i ++) {
if (a[i] == ...) {
i += 5;
}
}
In Rust, this does not work. The variable i will not be incremented by 5, but by 1 instead:
for i in 0..n {
if a[i] == ... {
i += 5;
}
}

You can create two parallel range iterators, zip them, then iterate though the combination:
fn main() {
let values = [10, 20, 30, 40, 50, 60, 70, 80, 90];
let lo = 2;
let mid = 5;
let hi = 7;
let early_indexes = lo..(mid + 1);
let late_indexes = (mid + 1)..(hi + 1);
for (i, j) in early_indexes.zip(late_indexes) {
println!("{}, {}", i, j);
println!("{} - {}", values[i], values[j]);
}
}
Someday, inclusive ranges will be stabilized, and you should be able to something like this (depending on the eventual syntax):
let early_indexes = lo...mid;
let late_indexes = (mid + 1)...hi;
for (i, j) in early_indexes.zip(late_indexes) {
println!("{}, {}", i, j);
println!("{} - {}", values[i], values[j]);
}
If you are actually iterating though a slice as I've shown for my example, you can also just combine the two iterators directly and ignore the index:
let early_values = values[lo..(mid + 1)].iter();
let late_values = values[(mid + 1)..(hi + 1)].iter();
for (i, j) in early_values.zip(late_values) {
println!("{}, {}", i, j);
}
The variable i will not be incremented by 5, but by 1 instead.
Yes, incrementing by a step is annoying, and some day it will also be stabilized. In the meantime:
What is a stable way to iterate on a range with custom step?
How do I iterate over a range with a custom step?
If you need full control, you can always use while or loop:
let mut i = 0;
while i < n {
if a[i] == ... {
i += 5;
}
i += 1;
}

Related

Getting "Use of moved value" in a loop

I'm making Tic-tac-toe in Rust. I want to count the amount of cells in a line, when the player is moving. Like -- x --. So I call count_result two times with the same dx and dy (that's why I pass dx and dy by reference). But here's the problem. My linter in IntelliJ IDEA complains that I use moved value. The compiler compiles without any warnings. What is the problem? Can you help me?
IntelliJ IDEA screenshot
Just to clarify - game_data.board is [[Option<Cell>; 3]; 3]
struct Data {
board: [[Option<Cell>; 3]; 3],
}
fn count_result(game_data: &Data, point: &Point, cell: &Cell, dx: &isize, dy: &isize) -> isize {
let mut count: isize = 0;
let point = (*point).clone();
let mut x = point.x;
let mut y = point.y;
while (x + dx < 3 && x + dx >= 0) && (y + dy >= 0 && y + dy < 3) {
x += 1;
y += 1;
if game_data.board[x as usize][y as usize] == Some(*cell) {
count += 1;
} else {
break;
}
}
count
}
Here is another example of the same problem.
game_data: &mut Data
IntelliJ IDEA screenshot
This is just a linter indexing invalid state. Just invalidate caches and you'll be fine.

How to compare i32 with usize

I'm writing a simple insert sort. Here are the relevant codes.
fn main() {
let mut sort_vec = vec![5,2,4,6,1,3];
for j in 1..sort_vec.len() {
let key = sort_vec[j];
let mut i = j - 1;
while i > 0 && sort_vec[i] > key {
sort_vec[i+1] = sort_vec[i];
i = i - 1;
}
sort_vec[i+1] = key;
}
println!("{:?}",sort_vec);
}
It's input is [5, 1, 2, 3, 4, 6].
The problem was when while i > 0 becomes while i >= 0 or while i > -1,it won't work.
So is there a problem comparing i32 with usize? I tried some methods and couldn't succeed. So how should I handle it? Be deeply grateful!
If you change while i > 0 to while i >= 0 the compiler gives you a warning:
warning: comparison is useless due to type limits
--> src\main.rs:9:15
|
9 | while i >= 0 && sort_vec[i] > key {
| ^^^^^^
|
= note: `#[warn(unused_comparisons)]` on by default
and the code panicks at runtime:
thread 'main' panicked at 'attempt to subtract with overflow', src\main.rs:11:17
The problem is that if i goes down to 0 and you try to subtract 1, the integer i overflows because its type is usize which has to be non-negative.
Because usize can't be negative your comparison i >= 0 is always true (that's the compiler warning).
I would recommend changing the logic a bit: Don't compare the second last element with the next one but compare the last element with the previous one.
So you have to start not at j - 1 but at j, and replace because of that in the following lines i in every index with i - 1:
sort_vec[i] -> sort_vec[i - 1]
sort_vec[i + 1] -> sort_vec[i]
working code:
fn main() {
let mut sort_vec = vec![5, 2, 4, 6, 1, 3];
for j in 1..sort_vec.len() {
let key = sort_vec[j];
let mut i = j;
while i > 0 && sort_vec[i - 1] > key {
sort_vec[i] = sort_vec[i - 1];
i = i - 1;
}
sort_vec[i] = key;
}
println!("{:?}", sort_vec);
}
Now you can see the unnecessary assignment let mut i = j. You could change the head of the for-loop to for mut j in ... to remove that and replace all i with j.
fn main() {
let mut sort_vec = vec![5, 2, 4, 6, 1, 3];
for mut j in 1..sort_vec.len() {
let key = sort_vec[j];
while j > 0 && sort_vec[j - 1] > key {
sort_vec[j] = sort_vec[j - 1];
j = j - 1;
}
sort_vec[j] = key;
}
println!("{:?}", sort_vec);
}
Here there's a nice trick (note that in Rust, wrapping arithmetic has to be explicit or it will panic in debug mode):
while i < j && sort_vec[i] > key {
sort_vec[i + 1] = sort_vec[i];
i = i.wrapping_sub(1);
}
sort_vec[i.wrapping_add(1)] = key;
Playground.
The idea is that we let i underflow then overflow back - but when it'll underflow it will be no longer less than j, so the loop will stop.
i has type usize, so it can never be less than 0. Therefore i >= 0 or i >= -1 are always true. One way to fix your issue is to use a for loop with a reversed range:
fn main() {
let mut sort_vec = vec![5, 2, 4, 6, 1, 3];
for j in 1..sort_vec.len() {
for i in (0..j).rev() {
if sort_vec[i+1] < sort_vec[i] {
sort_vec.swap (i, i+1);
} else {
break;
}
}
}
println!("{:?}", sort_vec);
}
Playground

Floating point addition algorithm

I'm attempting to write an addition algorithm of two floating point numbers in Rust. I have nearly got it to work, but there are a few cases when the final mantissa is one off from what it should be. (I'm not yet dealing with subnormal numbers). My algorithm is:
fn add_f32(a: f32, b: f32) -> f32 {
let a_bits = a.to_bits();
let b_bits = b.to_bits();
let a_exp = (a_bits << 1) >> (23 + 1);
let b_exp = (b_bits << 1) >> (23 + 1);
let mut a_mant = a_bits & 0x007fffff;
let mut b_mant = b_bits & 0x007fffff;
let mut a_exp = (a_exp as i32).wrapping_sub(127);
let mut b_exp = (b_exp as i32).wrapping_sub(127);
if b_exp > a_exp {
// If b has a larger exponent than a, swap a and b so that a has the larger exponent
core::mem::swap(&mut a_mant, &mut b_mant);
core::mem::swap(&mut a_exp, &mut b_exp);
}
let exp_diff = (a_exp - b_exp) as u32;
// Add the implicit leading 1 bit to the mantissas
a_mant |= 1 << 23;
b_mant |= 1 << 23;
// Append an extra bit to the mantissas to ensure correct rounding
a_mant <<= 1;
b_mant <<= 1;
// If the shift causes an overflow, the b_mant is too small so is set to 0
b_mant = b_mant.checked_shr(exp_diff).unwrap_or(0);
let mut mant = a_mant + b_mant;
let overflow = (mant >> 25) != 0;
if !overflow {
// Check to see if we round up
if mant & 1 == 1 {
mant += 1;
}
}
// check for overflow caused by rounding up
let overflow = overflow || (mant >> 25) != 0;
mant >>= 1;
if overflow {
if mant & 1 == 1 {
mant += 1;
}
// Check to see if we round up
mant >>= 1;
a_exp += 1;
}
// Remove implicit leading one
mant <<= 9;
mant >>= 9;
f32::from_bits(mant | ((a_exp.wrapping_add(127) as u32) << 23))
}
For example, the test
#[test]
fn test_add_small() {
let a = f32::MIN_POSITIVE;
let b = f32::from_bits(f32::MIN_POSITIVE.to_bits() + 1);
let c = add_f32(a, b);
let d = a + b;
assert_eq!(c, d);
}
fails, with the actual answer being 00000001000000000000000000000000 (binary representation) and my answer being 00000001000000000000000000000001.
Is anyone able to help me with what is wrong with my code?

How to avoid underflow pitfalls when iterating manually?

The code below would panic because j is typed as usize.
fn foo(vec: &Vec<i32>) {
let mut i = 0;
let mut j = vec.len() - 1;
while i < j {
while i < j && !some_condition(vec[i]) {
i += 1;
}
while i < j && !some_condition(vec[j]) {
j -= 1;
}
if i < j {
vec.swap(i, j);
i += 1;
j -= 1;
}
}
}
foo(&vec![]);
Most of the time iterator helps to avoid such issues. But when we have to manually iterate with indexes we really need to be careful. I could check the size of vec ahead to avoid panic. But the real problem here is I tend to think the program would work as expected even without empty check until a corner case bites me. So I wonder if there is any idiomatic way to do this kind of things in Rust.
You could look at saturating_sub or checked_sub:
fn foo(vec: &Vec<i32>) {
let mut i = 0;
let mut j = vec.len().saturating_sub(1);
while i < j {
// Some other logic
i += 1;
// EITHER
j.saturating_sub(1); // avoid getting below 0
//OR
j = j.checked_sub(1).expect("usize underflow");
}
}
foo(&vec![]);
If you want some more complex handling instead of .expect() you can use:
j = j.checked_sub(1).ok_or_else(|| some_your_error_creator())?;
which seems more idiomatic, but will require some additional work with error-handling.
Personally, I'd just do as you suggest vec.is_empty() and then return, to keep the logic clear.
If you don't want that, then instead you can perform a saturating subtraction using saturating_sub(). If the subtraction would result in an underflow then it would remain at 0.
// let mut j = vec.len() - 1;
let mut j = vec.len().saturating_sub(1);

Detect duplicated elements of a string slice happening in order

I need to detect and list string characters of slice that repeat themselves in order more or equal than N times. I managed to write non-higher-order-function solution in Rust already (below), but I wonder if this can be simplified to chaining iter methods.
The idea:
let v = "1122253225";
let n = 2;
Output:
There are 2 repetition of '1'
There are 3 repetition of '2'
There are 2 repetition of '2'
Indexes where repetition happens are not important. Repetition must happen in order (ie. 3 repetition of '2' separated by other values from the other 2 repetition of '2' counts as separate output lines).
My non-iterator solution:
let mut cur_ch = '\0';
let mut repeat = 0;
for ch in v.chars() {
if ch == cur_ch {
repeat = repeat + 1;
}
else {
if repeat >= n {
printf!("There are {} repetition of '{}'", repeat, cur_ch);
}
cur_ch = ch;
repeat = 1;
}
}
if repeat >= n {
printf!("There are {} repetition of '{}'", repeat, cur_ch);
}
It works, but is there a better way to do so with chaining iter methods?
Here is a solution that uses scan and filter_map:
fn main() {
let s = "112225322555";
let n = 2;
let i = s
.chars()
.map(|v| Some(v))
.chain(std::iter::once(None))
.scan((0, None), |(count, ch), v| match ch {
Some(c) if *c == v => {
*count += 1;
Some((None, *count))
}
_ => Some((ch.replace(v), std::mem::replace(count, 1))),
})
.filter_map(|(ch, count)| match ch {
Some(Some(ch)) if count >= n => Some((ch, count)),
_ => None,
});
for (ch, num) in i {
println!("There are {} repititions of {}", num, ch);
}
}
Playground Link
The first step is to use scan to count the number of adjacent characters. The first argument to scan is a state variable, which gets passed to each call of the closure that you pass as the second argument. In this case the state variable is a tuple containing the current character and the number of times it has been seen.
Note:
We need to extend the iteration one beyond the end of the string we are analyzing (otherwise we would miss the case where the end of the string contained a run of characters meeting the criteria). We do this by mapping the iteration into Option<char> and then chaining on a single None. This is better than special-casing a character such as \0, so that we could even count \0 characters.
For the same reason, we use Option<char> as the current character within the state tuple.
The return value of scan is an iterator over (Option<Option<char>>, i32). The first value in the tuple will be None for each repeated character in the iterator, whereas at each boundary where the character changes it will be Some(Some(char))
We use replace to both return the current character and count, at the same time as setting the state tuple to new values
The last step is to use filter_map to:
remove the (None, i32) variants, which indicate no change in the incoming character
filter out the cases where the count does not reach the limit n.
Here's one attempt at using filter_map():
fn foo(v: &str, n: usize) -> impl Iterator<Item = (usize, char)> + '_ {
let mut cur_ch = '\0';
let mut repeat = 0;
v.chars().chain(std::iter::once('\0')).filter_map(move |ch| {
if ch == cur_ch {
repeat += 1;
return None;
}
let val = if repeat >= n {
Some((repeat, cur_ch))
} else {
None
};
cur_ch = ch;
repeat = 1;
val
})
}
fn main() {
for (repeat, ch) in foo("1122253225", 2) {
println!("There are {} repetition of '{}'", repeat, ch);
}
}
And then you can generalize this to something like this:
fn foo<'i, I, T>(v: I, n: usize) -> impl Iterator<Item = (usize, T)> + 'i
where
I: Iterator<Item = T> + 'i,
T: Clone + Default + PartialEq + 'i,
{
let mut cur = T::default();
let mut repeat = 0;
v.chain(std::iter::once(T::default()))
.filter_map(move |i| {
if i == cur {
repeat += 1;
return None;
}
let val = if repeat >= n {
Some((repeat, cur.clone()))
} else {
None
};
cur = i;
repeat = 1;
val
})
}
This would be higher-order, but not sure if it's actually much simpler than just using a for loop!

Resources