Using loop labels

Rust allows us to label our loops. This can be very useful, for example with nested loops. These labels act as symbolic names for the loop and as we have a name for the loop, we can instruct the application to perform a task on that name.

Consider the following simple example:

// 04/looplabels/src/main.rs
fn main() { 'outer_loop: for x in 0..10 { 'inner_loop: for y in 0..10 { if x % 2 == 0 { continue 'outer_loop; } if y % 2 == 0 { continue 'inner_loop; } println!("x: {}, y: {}", x, y); } } }

What will this code do?

Here, x % 2 == 0 (or y % 2 == 0) means that if a variable divided by two returns no remainder, then the condition is met and it executes the code in the braces. When x % 2 == 0, or when the value of the loop is an even number, we will tell the application to skip to the next iteration of outer_loop, which is an odd number. However, we will also have an inner loop. Again, when y % 2 is an even value, we will tell the application to skip to the next iteration of inner_loop.

In this case, the application will output the following results:

While this example may seem very simple, it does allow for a great deal of speed when checking data. Let's go back to our previous example of data being sent to the web service. Recall that we have two values—the recorded data and some other value; for ease, it will be a data point. Each data point is recorded 0.2 seconds apart; therefore, every fifth data point is one second.

This time, we want all of the values where the data is greater than 1.5 and the associated time of that data point, but only on a time when it's dead on a second. As we want the code to be understandable and human-readable, we can use a loop label on each loop.

The following code is not quite correct. Can you spot why? The code compiles as follows:

// 04/looplabels-2/src/main.rs
fn main() { let my_array = vec![0.6f32, 0.4, 0.2, 0.8, 1.3, 1.1, 1.7, 1.9, 1.3, 0.1, 1.6, 0.6, 0.9, 1.1, 1.31, 1.49, 1.5, 0.7]; let my_time = vec![0.2f32, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8]; 'time_loop: for(_, time_value) in my_time.iter().enumerate() { 'data_loop: for(_, value) in my_array.iter().enumerate() { if *value < 1.5 { continue 'data_loop; } if *time_value % 5f32 == 0f32 { continue 'time_loop; } println!("Data point = {} at time {}s", *value, *time_value); } } }

This example is a very good one to demonstrate the correct operator in use. The issue is the if *time_value % 5f32 == 0f32 line. We are taking a float value and using the modulus of another float to see whether we end up with 0 as a float.

Comparing any value that is not a string, int, long, or bool type to another is never a good plan, especially if the value is returned by some form of calculation. We can also not simply use continue on the time loop, so how can we solve this problem?

If you recall, we're using _ instead of a named parameter for the enumeration of the loop. These values are always an integer; therefore, if we replace _ for a variable name, then we can use % 5 to perform the calculation and the code becomes the following:

'time_loop: for(time_enum, time_value) in my_time.iter().enumerate() 
    { 
       'data_loop: for(_, value) in my_array.iter().enumerate() 
       { 
           if *value < 1.5 
           { 
               continue 'data_loop; 
           } 
           if time_enum % 5 == 0 
           { 
               continue 'time_loop; 
           } 
           println!("Data point = {} at time {}s", *value, *time_value); 
       } 
    } 
 

The next problem is that the output isn't correct. The code gives the following:

Data point = 1.7 at time 0.4s 
Data point = 1.9 at time 0.4s 
Data point = 1.6 at time 0.4s 
Data point = 1.5 at time 0.4s 
Data point = 1.7 at time 0.6s 
Data point = 1.9 at time 0.6s 
Data point = 1.6 at time 0.6s 
Data point = 1.5 at time 0.6s 

The data point is correct, but the time is way out and continually repeats. We still need the continue statement for the data point step, but the time step is incorrect. There are a couple of solutions, but possibly the simplest will be to store the data and the time in a new vector and then display that data at the end.

The following code gets closer to what is required:

// 04/looplabels-3/src/main.rs
fn main() { let my_array = vec![0.6f32, 0.4, 0.2, 0.8, 1.3, 1.1, 1.7, 1.9, 1.3, 0.1, 1.6, 0.6, 0.9, 1.1, 1.31, 1.49, 1.5, 0.7]; let my_time = vec![0.2f32, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8]; let mut my_new_array = vec![]; let mut my_new_time = vec![]; 'time_loop: for(t, _) in my_time.iter().enumerate() { 'data_loop: for(v, value) in my_array.iter().enumerate() { if *value < 1.5 { continue 'data_loop; } else { if t % 5 != 0 { my_new_array.push(*value); my_new_time.push(my_time[v]); } } if v == my_array.len() { break; } } } for(m, my_data) in my_new_array.iter().enumerate() { println!("Data = {} at time {}", *my_data, my_new_time[m]); } }

We will now get the following output:

Data = 1.7 at time 1.4 
Data = 1.9 at time 1.6 
Data = 1.6 at time 2.2 
Data = 1.5 at time 3.4 
Data = 1.7 at time 1.4 

Yes, we now have the correct data, but the time starts again. We're close, but it's not right yet. We aren't continuing the time_loop loop and we will also need to introduce a break statement. To trigger the break, we will create a new variable called done. When v, the enumerator for my_array, reaches the length of the vector (this is the number of elements in the vector), we will change this from false to true. This is then tested outside of the data_loop. If done == true, break out of the loop.

The final version of the code is as follows:

// 04/dataloop/src/main.rs
fn main() { let my_array = vec![0.6f32, 0.4, 0.2, 0.8, 1.3, 1.1, 1.7, 1.9, 1.3, 0.1, 1.6, 0.6, 0.9, 1.1, 1.31, 1.49, 1.5, 0.7]; let my_time = vec![0.2f32, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6]; let mut my_new_array = vec![]; let mut my_new_time = vec![]; let mut done = false; 'time_loop: for(t, _) in my_time.iter().enumerate() { 'data_loop: for(v, value) in my_array.iter().enumerate() { if v == my_array.len() - 1 { done = true; } if *value < 1.5 { continue 'data_loop; } else { if t % 5 != 0 { my_new_array.push(*value); my_new_time.push(my_time[v]); } else { continue 'time_loop; } } } if done {break;} } for(m, my_data) in my_new_array.iter().enumerate() { println!("Data = {} at time {}", *my_data, my_new_time[m]); } }

Our final output from the code is this:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset