What is the exact definition of the for loop in Rust? - for-loop

I'm coming from a C (and to a lesser extent, C++) background. I wrote the following code snippet:
fn main() {
let my_array = [1, 2, 3];
let print_me = |j| println!("= {}", j);
for k in my_array.iter() {
print_me(k);
}
}
This compiled and ran as expected, but then I specified the type of the argument passed to the closure print_me thus:
fn main() {
let my_array = [1, 2, 3];
let print_me = |j: i32| println!("= {}", j);
for k in my_array.iter() {
print_me(k);
}
}
I got a compilation error:
error[E0308]: mismatched types
--> src/main.rs:6:22
|
6 | print_me(k);
| ^
| |
| expected i32, found &{integer}
| help: consider dereferencing the borrow: `*k`
|
= note: expected type `i32`
found type `&{integer}`
Now this confused me until I changed k to &k in the for statement, which worked fine:
fn main() {
let my_array = [1, 2, 3];
let print_me = |j: i32| println!("= {}", j);
for &k in my_array.iter() {
print_me(k);
}
}
It seems that I misunderstood the for syntax itself -- or maybe the exact workings of an iterator -- or maybe the usage syntax of a reference vis-a-vis a pointer [which are related but distinct in C++].
In the construct for A in B { C1; C2; ... Cn }, what exactly are A and B supposed to be?

First of all, here's a link to the definition of for in the reference.
To summarise, B is any expression which evaluates to something that can be converted into a value that implements the Iterator<T> trait, whilst A is a irrefutable pattern that binds values of type T.
In your specific case, slice::iter returns an Iter<i32>, which implements Iterator<Item = &i32>. That is, it doesn't yield i32s, it yields &i32s.
Thus, in both the first and second examples, k is actually binding to &i32s, not i32s. When you specified the type of the closure, you were actually specifying the wrong type. The reason the final example works is because A is a pattern, not a variable name. What &k is actually doing is "de-structuring" the &i32, binding the i32 part to a variable named k.
The "irrefutable" part simply means that the pattern must always work. For example, you can't do for Some(x) in thingy where thingy implements Iterator<Option<_>>; Some(x) would not necessarily be valid for every element in the iterator; thus, it's a refutable pattern.

Many iterators actually return a reference rather than a value. To be sure, you have to check the return type of .iter(), which should be of the form Iterator<Item = X>: X will be the type of the variable returned.
So here:
fn main() {
let my_array = [1, 2, 3];
let print_me = |j: i32| println!("= {}", j);
for k in my_array.iter() {
print_me(k);
}
}
This X is &i32 (a reference to i32), and therefore k has type &i32.
This is why, when calling print_me, there is an error: &i32 is passed where i32 is expected.
There are multiple possible fixes here:
specify a different type to print_me:
let print_me = |j: &i32| println!("= {}", j);
dereference the value of k:
print_me(*k);
change the type of k by destructuring in the loop:
for &k in my_array.iter() { ... }
The destructuring occurs because for .. in accepts an irrefutable pattern, so you can pattern match like you would do in a match expression, except that the variable's type has to match (otherwise you get a compiler time error).
To better illustrate it, we can use a slightly more complicated example:
fn main() {
let my_array = [(1, 2), (2, 3), (3, 4)];
let print_me = |a: i32, b: i32| println!("= {} {}", a, b);
for &(j, k) in my_array.iter() {
print_me(j, k)
}
}
The type of my_array is [(i32, i32)]: an array of tuples of 2 i32. The result of .iter() is therefore of type Iterator<Item = &(i32, i32)>: an iterator to a reference to a tuple of 2 i32 aka &(i32, i32).
When we use the irrefutable pattern &(j, k) what happens is that we destructure the tuple so that:
the first element binds to j (inferred to be of type i32, only works because i32 is Copy)
the second element binds to k ((inferred to be of type i32)
j and k thus become temporary copies of the i32 inside this element.

Related

Why do I get different argument types when using filter on an iterator?

I'm using filter, but I don't understand why I should use **x > 1 for a slice but use *x > 1 for a range.
fn main() {
let a = [0, 1, 2, 3];
let a_iter = a.iter().filter(|x: &&i32| **x > 1); // x: &&i32
let x: Vec<&i32> = a_iter.collect();
println!("{:?}", x);
let b = 0..4;
let b_iter = b.filter(|x: &i32| *x > 1); // x: &i32
let y: Vec<i32> = b_iter.collect();
println!("{:?}", y);
}
The docs say it should be **x > 1.
slice::iter, such as a.iter() in your example, produces an iterator over references to values. Ranges are iterators that produce non-reference values.
The filter(<closure>) method takes a <closure> that takes iterator values by reference, so if your iterator already produces references you'll get a reference to a reference, and if your iterator produces non-reference values then you'll get references to those values.
The difference becomes easier to understand if we use a Vec for both examples:
fn main() {
let a = vec![0, 1, 2, 3];
let a_iter = a.iter(); // iter() returns iterator over references
let x: Vec<&i32> = a_iter.filter(|x: &&i32| **x > 1).collect();
println!("{:?}", x);
let b = vec![0, 1, 2, 3];
let b_iter = a.into_iter(); // into_iter() returns iterator over values
let y: Vec<i32> = b_iter.filter(|x: &i32| *x > 1).collect();
println!("{:?}", y);
}
playground
This is because in the array-example, you first call .iter() to create an iterator, which borrows the array and hence to values in it; the closure-argument to filter receives a borrowed version of the iterator's Item, so it's borrowed again, which makes it a &&i32.
In the Range-case, you call filter directly, as a Range is an iterator. The iterator, therefore, contains owned values and the closure in filter borrows that, which makes its type &i32.
You can see that if you try to let y = b; after the let b_iter = ...-line: You'll get a use of moved value-error because b was consumed by the iterator you used in b.filter

When to use references in for loops?

An example from the documentation about vectors:
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!("The third element is {}", third);
match v.get(2) {
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}
I can't see why third needs to be a reference. let third: i32 = v[2] seems to work just as well. What does making it a reference achieve?
Similarly:
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
why is it in &v instead of just in v?
let third: i32 = v[2] works because i32 implements Copy trait. They don't get moved out when indexing the vector, they get copied instead.
When you have a vector of non Copy type, it is a different story.
let v = vec![
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string(),
"5".to_string(),
];
let third = &v[2]; // This works
// let third = v[2]; // This doesn't work because String doesn't implement Copy
As for the second question about the loop, for loop is syntactic sugar for IntoIterator which moves and consumes.
So, when you need to use v after the loop, you don’t want to move it. You want to borrow it with &v or v.iter() instead.
let v = vec![100, 32, 57];
for i in &v { // borrow, not move
println!("{}", i);
}
println!("{}", v[0]); // if v is moved above, this doesn't work

Tuple assignment to mutable struct parameters [duplicate]

This question already has answers here:
Can I destructure a tuple without binding the result to a new variable in a let/match/for statement?
(3 answers)
How to swap two variables?
(2 answers)
Closed 3 years ago.
I’m getting an error I’m not sure how to handle, related to a tuple assignment I’m trying. The incr function below gets a left-hand of expression not valid error. What am I misunderstanding?
struct Fib {
i: u64,
fa: u64,
fb: u64,
}
impl Fib {
fn incr(&mut self) {
self.i += 1;
(self.fa, self.fb) = (self.fa + self.fb, self.fa);
}
}
As the helpful error explanation says†, you try to assign to a non-place expression. A place expression represents a memory location, and thus it can be a variable, a dereference, an indexing expression or a field reference, but a tuple is not one of these.
If you would use a binding, such as:
let (x, y) = (1, 2);
that would be a whole different story because let statements have different rules than assignment: the left hand side of a let statement is a pattern, not an expression, and (x, y) is a legal pattern.
To solve your problem, you may want to do the following and introduce a temporary variable, and then update the values of the members:
(The following is also fixing your fibonacci sequence, i.e. correcting the values of the members since they are naturally ordered as 'a' and 'b')
impl Fib {
fn incr(&mut self) {
self.i += 1;
let fa = self.fa;
self.fa = self.fb;
self.fb += fa;
}
}
Note: Albeit it was not your question, I would strongly advise to implement Iterator for your Fib type, in which case you wouldn't have to keep track of the index (i) because that would be available through the enumerate method.
E.g.
impl Iterator for Fib {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
let fa = self.fa;
self.fa = self.fb;
self.fb += fa;
Some(fa)
}
}
And then you could use it as:
for (i, x) in my_fib.enumerate() { ... }
† rustc --explain E0070

How to iterate and extract values out of a for loop in Rust

I'm new to Rust and looking to understand concepts like borrowing. I'm trying to create a simple two dimensional array using standard input. The code:
use std::io;
fn main() {
let mut values = [["0"; 6]; 6]; // 6 * 6 array
// iterate 6 times for user input
for i in 0..6 {
let mut outputs = String::new();
io::stdin().read_line(&mut outputs).expect(
"failed to read line",
);
// read space separated list 6 numbers. Eg: 5 7 8 4 3 9
let values_itr = outputs.trim().split(' ');
let mut j = 0;
for (_, value) in values_itr.enumerate() {
values[i][j] = value;
j += 1;
}
}
}
This won't compile because the outputs variable lifetime is not long enough:
error[E0597]: `outputs` does not live long enough
--> src/main.rs:20:5
|
14 | let values_itr = outputs.trim().split(' ');
| ------- borrow occurs here
...
20 | }
| ^ `outputs` dropped here while still borrowed
21 | }
| - borrowed value needs to live until here
How can I get the iterated values out of the block into values array?
split() gives you substrings (string slices) borrowed from the original string, and the original string is outputs from line 6.
The string slices can't outlive the scope of outputs: when a loop iteration ends, outputs is deallocated.
Since values is longer lived, the slices can't be stored there.
We can't borrow slices of outputs across a modification of outputs. So even if the String outputs itself was defined before values, we couldn't easily put the string slices from .split() into values; modifying the string (reading into it) invalidates the slices.
A solution needs to either
Use a nested array of String, and when you assign an element from the split iterator, make a String from the &str using .to_string(). I would recommend this solution. (However an array of String is not at as easy to work with, maybe already this requires using Vec instead.) 1
Read all input before constructing a nested array of &str that borrows from the input String. This is good if the nested array is something that you only need temporarily.
1: You can use something like vec![vec![String::new(); 6]; 6] instead
This answer was moved from the question, where it solved the OPs needs.
use std::io;
fn main() {
let mut values = vec![vec![String::new(); 6]; 6];
for i in 0..6 {
let mut outputs = String::new();
io::stdin().read_line(&mut outputs)
.expect("failed to read line");
let values_itr = outputs.trim().split(' ');
let mut j = 0;
for (_, value) in values_itr.enumerate() {
values[i][j] = value.to_string();
j += 1;
}
}
}

Why does a range that starts at a negative number not iterate?

I have just started to learn Rust. During my first steps with this language, I found a strange behaviour, when an iteration is performed inside main or in another function as in following example:
fn myfunc(x: &Vec<f64>) {
let n = x.len();
println!(" n: {:?}", n);
for i in -1 .. n {
println!(" i: {}", i);
}
}
fn main() {
for j in -1 .. 6 {
println!("j: {}", j);
}
let field = vec![1.; 6];
myfunc(&field);
}
While the loop in main is correctly displayed, nothing is printed for the loop inside myfunc and I get following output:
j: -1
j: 0
j: 1
j: 2
j: 3
j: 4
j: 5
n: 6
What is the cause of this behaviour?
Type inference is causing both of the numbers in your range to be usize, which cannot represent negative numbers. Thus, the range is from usize::MAX to n, which never has any members.
To find this out, I used a trick to print out the types of things:
let () = -1 .. x.len();
Which has this error:
error: mismatched types:
expected `core::ops::Range<usize>`,
found `()`
(expected struct `core::ops::Range`,
found ()) [E0308]
let () = -1 .. x.len();
^~
Diving into the details, slice::len returns a usize. Your -1 is an untyped integral value, which will conform to fit whatever context it needs (if there's nothing for it to conform to, it will fall back to an i32).
In this case, it's as if you actually typed (-1 as usize)..x.len().
The good news is that you probably don't want to start at -1 anyway. Slices are zero-indexed:
fn myfunc(x: &[f64]) {
let n = x.len();
println!(" n: {:?}", n);
for i in 0..n {
println!(" i: {}", i);
}
}
Extra good news is that this annoyance was fixed in the newest versions of Rust. It will cause a warning and then eventually an error:
warning: unary negation of unsigned integers will be feature gated in the future
for i in -1 .. n {
^~
Also note that you should never accept a &Vec<T> as a parameter. Always use a &[T] as it's more flexible and you lose nothing.

Resources