[英]Rust borrowing rules in `for...in` loop
Why would all these three print_max
functions work?为什么这三个
print_max
函数都能工作? Which one is the best practice?哪一个是最佳实践? Is
for number in number_list
a shortcut for for number in number_list.iter()
? for number in number_list
的for number in number_list.iter()
for number in number_list
的快捷方式吗?
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
print_max_1(&number_list);
print_max_2(&number_list);
print_max_3(&number_list);
}
fn print_max_1(number_list: &[u16]) {
let mut largest = &number_list[0]; // borrow the first number
for number in number_list.iter() { // borrowing?
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
fn print_max_2(number_list: &[u16]) {
let mut largest = &number_list[0]; // borrow the first number
for number in number_list { // shortcut for .iter()?
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
fn print_max_3(number_list: &[u16]) {
let mut largest = number_list[0]; // copying?
for &number in number_list.iter() { // borrowing?
if number > largest { // no error
largest = number;
}
}
println!("The largest number is {}", largest);
}
Why wouldn't this work?为什么这行不通?
fn print_max_4(number_list: &[u16]) {
let mut largest = &number_list[0];
for &number in number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
The error message says that largest
is &u16
while number
is u16
.错误消息说
largest
是&u16
而number
是u16
。 Why isn't number
&u16
?为什么不是
number
&u16
?
Let's tackle these one-by-one.让我们一一解决这些问题。
print_max_1
Here, largest
is a mutable variable that holds an immutable reference to a u16
(ie it holds a &u16
).这里,
largest
是一个可变变量,它持有对u16
的不可变引用(即它持有&u16
)。 Within the loop, number
is also a &u16
which, like largest
, is borrowed from number_list
.在循环中,
number
也&u16
其中,像largest
,从借来的number_list
。 Both number
and larger
are implicitly dereferenced to perform the comparison. number
和larger
都被隐式取消引用以执行比较。 If the value referenced by number
is greater than that referenced by larger
, you store a copy of the immutable reference contained in number
into largest
.如果引用的值
number
比通过引用更大larger
,你存储包含处于不可改变引用的副本number
为largest
。
print_max_2
In this case, since number_list
is itself borrowed, the analysis of print_max_2
is identical to print_max_1
.在这种情况下,由于
number_list
本身借用的分析print_max_2
相同print_max_1
。
print_max_3
Here, largest
is a u16
.这里,
largest
是一个u16
。 You are correct that number_list[0]
is copied, but it is worth noting that this copy is cheap .复制
number_list[0]
是正确的,但值得注意的是,此副本很便宜。 Within the loop, each element of number_list
is copied and stored directly in number
.在循环中,
number_list
每个元素number_list
被复制并直接存储在number
。 If number
is greater than largest
, you stored the new greatest value directly in largest
.如果
number
大于largest
,则将新的最大值直接存储在largest
。 This is the most optimal of the three implementations you've written, since you do away with all of the unnecessary indirection that references (ie, pointers) introduce.这是您编写的三个实现中最优化的,因为您消除了引用(即指针)引入的所有不必要的间接性。
print_max_4
Once again, you store a reference to the first element of number_list
in largest
, ie largest
is a &u16
.再一次,您将
number_list
的第一个元素的引用存储在largest
,即largest
是一个&u16
。 Similarly, as was the case in print_max_3
, number
is a u16
, which will hold copies of the elements from number_list
.类似地,与
print_max_3
中的情况一样, number
是u16
,它将保存number_list
元素的number_list
。 However, as you noted, this function is the problem child.但是,正如您所指出的,此功能是问题子项。
Within the loop, the compiler will point out two errors:在循环中,编译器会指出两个错误:
PartialOrder
defined, namely largest
which is a &u16
and number
which is a u16
.PartialOrder
不同类型,即largest
是&u16
和number
是u16
。 Rust isn't in the business of trying to guess what you mean by this comparison, so in order fix this, you'll have to make both number
and largest
the same type. number
和largest
设为相同的类型。 In this case, what you want to do is explicitly dereference largest
using the *
operator, which will allow you to compare the u16
it points to with the u16
contained in number
.largest
使用*
运算符,它可以让你比较u16
它指向与u16
包含在number
。 This final comparison looks likeif number > *largest { ... }
u16
in a variable of type &u16
, which does not make sense.u16
存储在&u16
类型的变量中,这是没有意义的。 Unfortunately, here you're going to run into a wall.number_list
, but largest
needs to hold a reference to a u16
.number_list
复制的数字的值,但largest
需要保存对u16
的引用。 We can't simply borrow number
here (eg by writing largest = &number
), since number
will be dropped (ie go out of scope) at the end of the loop.number
(例如,通过编写largest = &number
),因为number
将在循环结束时被删除(即超出范围)。 The only way to resolve is is to revert by to print_max_2
by storing the maximum value itself instead of the pointer to it.print_max_2
通过存储最大值本身,而不是指向它的指针。 As for whether for number in number_list
is a shortcut for for number in number_list.iter()
, the answer is a firm no .至于 for
for number in number_list
是否是for number in number_list.iter()
的快捷方式,答案是否定的。 The former will take ownership of number_list
, and during each iteration, number
takes ownership of the next value in number_list
.前者将拥有
number_list
所有权,并且在每次迭代期间, number
将拥有number_list
下一个值的number_list
。 In contrasts, the latter only performs a borrow, and during each iteration of the loop, number
receives an immutable reference to the next element of number_list
.相比之下,后者只执行借位,并且在循环的每次迭代期间,
number
都会收到对number_list
下一个元素的不可变引用。
In this specific case, these two operation appear identical, since taking ownership of an immutable reference simply entails making a copy, which leaves the original owner intact.在这种特定情况下,这两个操作看起来是相同的,因为获得不可变引用的所有权只需要制作一个副本,从而使原始所有者保持不变。 For more information, see this answer to a related question on the difference between
.into_iter()
and .iter()
.有关更多信息,请参阅有关
.into_iter()
和.iter()
之间区别的相关问题的答案。
There are a few things happening auto-magically here to note:这里有一些事情会自动神奇地发生:
Your variable 'number_list' is a std::vec::Vec
.您的变量 'number_list' 是
std::vec::Vec
。 You then use a slice for the function argument signatures.然后将切片用于函数参数签名。 Vector has an implementation for the
Deref
trait. Vector 有一个
Deref
trait 的实现。 In rust, this particular arrangement uses Deref coercion
to convert the vector with mentioned Deref trait into a std::slice
.在
Deref coercion
,这种特殊的安排使用Deref coercion
转换将具有提到的 Deref 特征的向量转换为std::slice
。
However, both vectors and slices can be iterated by using a for loop.但是,向量和切片都可以使用 for 循环进行迭代。 Anything that implements the
std::iter::Iterator
trait.任何实现
std::iter::Iterator
特性的东西。 The vector doesn't do this, but rather implements std::iter::IntoIterator
, which as it puts it, By implementing IntoIterator for a type, you define how it will be converted to an iterator. This is common for types which describe a collection of some kind.
vector 不这样做,而是实现
std::iter::IntoIterator
,正如它所说的那样, By implementing IntoIterator for a type, you define how it will be converted to an iterator. This is common for types which describe a collection of some kind.
By implementing IntoIterator for a type, you define how it will be converted to an iterator. This is common for types which describe a collection of some kind.
Take a look at Implementing an Iterator for more details.有关更多详细信息,请查看实现迭代器。 Specifically the lines:
具体线路:
all
Iterators
implementIntoIterator
, by just returning themselves.所有
Iterators
IntoIterator
通过返回自身来实现IntoIterator
。 This means two things:这意味着两件事:
If you're writing an Iterator, you can use it with a for loop.
如果您正在编写迭代器,则可以将它与 for 循环一起使用。 If you're creating a collection, implementing
IntoIterator
for it will allow your collection to be used with the for loop.如果您正在创建一个集合,为它实现
IntoIterator
将允许您的集合与 for 循环一起使用。
You'll find that Rust provides a lot of conversion traits, and some automatic hidden behavior.你会发现 Rust 提供了很多转换特性,以及一些自动隐藏的行为。 With the extensive type use in Rust, this helps alleviate some of the necessary conversions.
由于 Rust 中广泛使用类型,这有助于减轻一些必要的转换。 See From and Into for more details there.
有关更多详细信息,请参阅From 和 Into 。
I hated this about C++ (hidden code), however it's not so bad in Rust.我讨厌 C++(隐藏代码),但是在 Rust 中它并没有那么糟糕。 If the compiler lets you do it, then you've probably found what you need.
如果编译器允许您这样做,那么您可能已经找到了您需要的东西。 Sometimes, the automatic way may not be adequate, and you may need to use supporting methods/functions to get there.
有时,自动方式可能不够用,您可能需要使用支持方法/功能来实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.