何时进行取消引用或不进行

15

我正在阅读 "Rust Book" 网站,以便学习这门语言,为即将到来的工作面试做准备。在关于向量的章节中,有两个代码示例:

fn main() {
    let v = vec![100, 32, 57];
    for i in &v {
        println!("{}", i);
    }
}

并且:

fn main() {
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }
}

现在我想知道,为什么在第一个示例中,当我们将向量元素 i 的引用传递给:

println!("{}", i);

但是在将 50 添加到向量的每个元素的示例中,我们需要在加上 50 之前解引用元素吗?

为什么不能这样做:

fn main() {
    let v = vec![100, 32, 57];
    for i in &v {
        println!("{}", *i); // why don't we have to dereference before we pass to println!?
    }
}

或者:
fn main() {
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        i += 50; // why can't we just add directly to the reference like this?
    }
}

我可能误解了我所读的内容,但我认为Rust能自动识别何时需要解引用,何时不需要。我猜我不理解为什么我们需要在这两个示例中解引用(或不解引用)。 我提供的两个示例都有我想知道的特定代码注释。


这个回答解决了你的问题吗?Rust 的自动解引用规则是什么? - Herohtar
还有相关内容:https://dev59.com/g14b5IYBdhLWcg3wYQqZ - Herohtar
Herohtar提供的链接答案似乎没有解决println!自动引用任何你尝试格式化的值的事实。如果你想打印一个地址,你必须将其引用转换为原始指针。 - rodrigo
1
是的,我是说那些有点帮助,但我仍然不完全理解。那个答案对我来说相当复杂。我还是非常非常初级的水平。也许一旦我深入学习 Rust 书籍,我会知道更多,但我想问一下,以防这些概念在书中后面变得非常重要。 - Austin Wile
1个回答

7

我认为最简单的理解方法是,第二个例子是“正常”的。

fn main() {
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }
}

i&mut i32(只有i32,因为没有其他整数类型可推断),因此要对其进行赋值,需要将其解引用为mut i32

println!的示例是做一些“魔术”的。 println!将格式化传递的类型无论它们是按值传递还是按引用传递。这非常方便,您不希望(例如)克隆每个想要打印但稍后在应用程序中使用的字符串。


编辑:

为了完整起见,这种“魔法”实际上并不是魔法,而是很好地使用了语言特性。println!(以及每个执行格式化的标准宏,例如panic!format!)都使用标准库中的格式化机制。这可以与实现Display trait(或如果使用{:?}则为Debug trait)的任何类型一起使用。而Display已经为所有实现Display的事物的引用提供了覆盖实现Debug也是如此):

impl<'_, T> Display for &'_ T where
    T: Display + ?Sized, 
{ /* ... */ }

所以任何您可以使用值格式化的内容,也可以使用引用格式化。


这是一个很好的解释。只是为了参考(开个玩笑,哈哈),"magic"指的是deref coercion吗?这是我在最近学习Rust时在各种地方读到过的东西。这是它的意思吗,还是deref coercion完全不同的东西? - Austin Wile
1
@AustinWile 在这种情况下不是这样的,因为编辑表明“magic”的只是Display在所有&T where T: Display上实现并且在幕后委托给T。因此,它是一种明确实现的行为,而不是任何语言默认。 - Masklinn
1
@AustinWile 不是。这里很好地描述了Deref coercion,它涉及到Deref trait和引用&A在需要&B的情况下也是可以接受的。 - JMAA
1
解引用强制转换是指编译器本身会隐式添加解引用以使类型匹配,例如 foo.bar() 将调用 Foo::bar(&self) 即使 foo&&&&Foo,因为在解析方法调用时编译器会添加解引用直到找到匹配项。这也是为什么可以通过例如 &String 直接访问 str 的方法,而无需显式解引用(String 实际上是一个智能指针)。 - Masklinn
感谢您提供有用的指导(不是故意双关语,或者说是吗?:D)! - Austin Wile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接