在Rust中for循环中`&`和`ref`的区别是什么

ux6nzvsh  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(184)
  • Rust By Example* 说

赋值左侧的ref借用等效于右侧的&借用。
现在:

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?
}

字符串
我想知道for ref j in v循环中的j到底是什么。

fwzugrvs

fwzugrvs1#

确实,let ref foo = bar;等价于let foo = &bar;。但是在for循环中有一个不同的机制。一般而言

for foo in bar {}

字符串
使用IntoIterator特征将bar“转换”为迭代器。所以当你写

let v = vec![];
for foo in v {}


rust将使用实现Vec IntoIterator,这将产生T s。但是如果你写

let v = vec![];
for foo in &v {}


rust将对&Vec使用实现实现IntoIterator,这将产生&T s。请注意,也有&mut Vec的实作。
for foo in barfoo将绑定到迭代器返回值这是因为for循环是一种语法糖,它将这种语法糖分解为以下内容(请参阅文档):

{
    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
}


再回到我们的例子。for foo in &vfoo绑定到对向量元素的引用。这就是为什么它们都有自己的连续地址(注意,每次迭代增加4个字节,大小为i32)。但是,当我们说for ref foo in barref foo将绑定到堆栈分配变量时,迭代器返回的值将在每次迭代中移动到该变量中(参见上面的next)。由于在每次迭代中使用相同的堆栈变量,因此它们都将具有相同的地址,但每次都会更改值。

z9smfwbn

z9smfwbn2#

我想你对ref的理解已经足够好了。然而,有一些神奇的事情发生了,允许你引用一个临时变量。
例如,看看这段代码。

let ref a = "hello".to_string();

字符串
ref a是一个扩展模式。这意味着字符串存储在一个临时的、未命名的绑定中,该绑定将在此范围结束时被删除。你可以这样想象:

let temp = "hello".to_string();
let ref a = temp;


这解释了为什么j总是相同的地址。请注意,for _ in v消耗v并迭代所拥有的值。无论in之前的模式如何,都会发生这种情况。换句话说,let a = &blet ref a = b相同,但for a in &bfor ref a in b不同。你可以重写for循环,看起来像这样:

let mut v_iter = v.into_iter();
loop {
    let temp = match v_iter.next() {
        Some(x) => x,
        None => break,
    };
    let ref j = temp;
    println!("{:p}", j);
}


被打印的地址是temp的地址,它在每次迭代中存储一个拥有的值。创建j时,该值既不在v中,也不在v_iter中。

相关问题