在迭代向量时推送到向量时,Rust 会保护我免受迭代器失效吗?
Does Rust protect me from iterator invalidation when pushing to a vector while iterating over it?
Rust 是否保护我免受迭代器失效的影响,还是我只是幸运 realloc
? &'a Vec<T>
返回的迭代器有什么保证?
fn main() {
let mut v = vec![0; 2];
println!("capacity: {}", v.capacity());
{
let v_ref = &mut v;
for _each in v_ref.clone() {
for _ in 0..101 {
(*v_ref).push(1); // ?
}
}
}
println!("capacity: {}", v.capacity());
}
在 Rust 中,大多数方法采用 &self
- reference 到 self。在大多数情况下,像 some_string.len()
这样的调用在内部 "expands" 是这样的:
let a: String = "abc".to_string();
let a_len: usize = String::len(&a); // This is identical to calling `a.len()`.
但是,考虑对对象的引用:a_ref
,它是引用 a
的 &String
。 Rust 足够聪明,可以确定是否需要添加或删除引用,就像我们在上面看到的那样(a
变为 &a
);在这种情况下,a_ref.len()
扩展为:
let a: String = "abc".to_string();
let a_ref: &String = &a;
let a_len: usize = String::len(a_ref); // This is identical to calling `a_ref.len();`. Since `a_ref` is a reference already, it doesn't need to be altered.
请注意,这基本上等同于原始示例,除了我们使用 explicitly-set 引用 a
而不是直接 a
。
这意味着 v.clone()
扩展为 Vec::clone(&v)
,类似地,v_ref.clone()
扩展为 Vec::clone(v_ref)
,并且由于 v_ref
是 &v
(或者,具体来说,&mut v
),我们可以将其简化为 Vec::clone(&v)
。换句话说,这些调用是 等效的 - 在对象的基本引用 (&
) 上调用 clone()
不会克隆 reference,它克隆引用的对象。
换句话说,Tamas Hedgeus 的评论是正确的:您正在迭代一个新向量,其中包含的元素是 v
中元素的克隆。 for
循环中迭代的项目不是 &Vec
,而是与 v
分开的 Vec
,因此迭代器失效不是问题。
关于你关于 Rust 提供的保证的问题,你会发现 Rust 的借用检查器处理得相当好,没有任何附加条件。
如果您在没有 v_ref
抽象的情况下 remove clone()
from the for
loop, though, you would receive an error message, use of moved value: '*v_ref'
, because v_ref
is considered 'moved' into the for
loop when you iterate over it, and cannot be used for the remainder of the function; to avoid this, the iter
function creates an iterator object that only borrows the vector, allowing you to reuse the vector after the loop ends (and the iterator is dropped). And if you were to try iterating over and mutating v
,则错误显示为 cannot borrow 'v' as mutable because it is also borrowed as immutable
。 v
在由 v.iter()
产生的迭代器中被不变地借用(它具有 fn iter(&self) -> Iter<T>
的类型签名 - 注意,它使 borrow 到向量) , 并且不允许你因为 Rust 的借用检查器而改变向量,直到迭代器被删除(在 for
循环的末尾)。但是,由于您可以有多个 immutable 对单个对象的引用,您仍然可以 read 从 for 循环中的向量,只是不写入它。
如果你需要在迭代向量时改变向量的元素,你可以使用iter_mut
,其中returnsmutable 一次引用一个元素并允许您更改该元素仅。你仍然不能用 iter_mut
改变迭代向量本身,因为 Rust 确保一次只有 one 个对象的可变引用,并确保没有可变引用对对象的引用与对该对象的不可变引用在同一范围内。
Rust 是否保护我免受迭代器失效的影响,还是我只是幸运 realloc
? &'a Vec<T>
返回的迭代器有什么保证?
fn main() {
let mut v = vec![0; 2];
println!("capacity: {}", v.capacity());
{
let v_ref = &mut v;
for _each in v_ref.clone() {
for _ in 0..101 {
(*v_ref).push(1); // ?
}
}
}
println!("capacity: {}", v.capacity());
}
在 Rust 中,大多数方法采用 &self
- reference 到 self。在大多数情况下,像 some_string.len()
这样的调用在内部 "expands" 是这样的:
let a: String = "abc".to_string();
let a_len: usize = String::len(&a); // This is identical to calling `a.len()`.
但是,考虑对对象的引用:a_ref
,它是引用 a
的 &String
。 Rust 足够聪明,可以确定是否需要添加或删除引用,就像我们在上面看到的那样(a
变为 &a
);在这种情况下,a_ref.len()
扩展为:
let a: String = "abc".to_string();
let a_ref: &String = &a;
let a_len: usize = String::len(a_ref); // This is identical to calling `a_ref.len();`. Since `a_ref` is a reference already, it doesn't need to be altered.
请注意,这基本上等同于原始示例,除了我们使用 explicitly-set 引用 a
而不是直接 a
。
这意味着 v.clone()
扩展为 Vec::clone(&v)
,类似地,v_ref.clone()
扩展为 Vec::clone(v_ref)
,并且由于 v_ref
是 &v
(或者,具体来说,&mut v
),我们可以将其简化为 Vec::clone(&v)
。换句话说,这些调用是 等效的 - 在对象的基本引用 (&
) 上调用 clone()
不会克隆 reference,它克隆引用的对象。
换句话说,Tamas Hedgeus 的评论是正确的:您正在迭代一个新向量,其中包含的元素是 v
中元素的克隆。 for
循环中迭代的项目不是 &Vec
,而是与 v
分开的 Vec
,因此迭代器失效不是问题。
关于你关于 Rust 提供的保证的问题,你会发现 Rust 的借用检查器处理得相当好,没有任何附加条件。
如果您在没有 v_ref
抽象的情况下 remove clone()
from the for
loop, though, you would receive an error message, use of moved value: '*v_ref'
, because v_ref
is considered 'moved' into the for
loop when you iterate over it, and cannot be used for the remainder of the function; to avoid this, the iter
function creates an iterator object that only borrows the vector, allowing you to reuse the vector after the loop ends (and the iterator is dropped). And if you were to try iterating over and mutating v
,则错误显示为 cannot borrow 'v' as mutable because it is also borrowed as immutable
。 v
在由 v.iter()
产生的迭代器中被不变地借用(它具有 fn iter(&self) -> Iter<T>
的类型签名 - 注意,它使 borrow 到向量) , 并且不允许你因为 Rust 的借用检查器而改变向量,直到迭代器被删除(在 for
循环的末尾)。但是,由于您可以有多个 immutable 对单个对象的引用,您仍然可以 read 从 for 循环中的向量,只是不写入它。
如果你需要在迭代向量时改变向量的元素,你可以使用iter_mut
,其中returnsmutable 一次引用一个元素并允许您更改该元素仅。你仍然不能用 iter_mut
改变迭代向量本身,因为 Rust 确保一次只有 one 个对象的可变引用,并确保没有可变引用对对象的引用与对该对象的不可变引用在同一范围内。