Rust By Example says
A
ref
borrow on the left side of an assignment is equivalent to an&
borrow on the right side.
Now:
let u = 42u32;
let x = &u;
println!("{:p}",x);
let ref y = u;
println!("{:p}", y);
// same addr printed and that's expected
let v = [1, 2, 3];
for i in &v {
println!("{:p}", i);
// 0x7fff4ae23334
// 0x7fff4ae23338
// 0x7fff4ae2333c
// ok, still expected
}
for ref j in v {
println!("{:p}",j);
// SAME addr (and not 0x7fff4ae23334) printed for each round, WHY?
}
I wonder what exactly is j
here in the for ref j in v
loop.
It is true that let ref foo = bar;
is equivalent to let foo = &bar;
. However in for loops there is a different mechanism. In general
for foo in bar {}
uses IntoIterator trait to "convert" bar
to an iterator. So when you write
let v = vec![];
for foo in v {}
rust will use implementation impl IntoIterator for Vec, which will yield T
s. However if you write
let v = vec![];
for foo in &v {}
rust will use implementation impl IntoIterator for &Vec, which will yield &T
s. Note that there is also implementation for &mut Vec.
foo
in for foo in bar
will bind to values returned by iterator. This is because for
loop is a syntactic sugar that de-sugars into this (see documentation):
{
let result = match IntoIterator::into_iter(bar) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let foo = next; /* here is the binding we provided in the for loop */
let () = { /* loop body */ };
},
};
result
}
Going back to our example. for foo in &v
will bind foo
to references to the elements of vector. This is why they each have it's own consecutive address (note that it increases by 4 bytes in each iteration - the size of i32
). But when we say for ref foo in bar
ref foo
will bind to the stack allocated variable into which value returned by the iterator is moved in each iteration (see next
above). Since the same stack variable is used in each iteration they all will have the same addresses, however value will be changed each time.