在迭代时推送到向量时,Rust 是否可以保护我免受迭代器失效的影响?

Gre*_*ree 2 iterator vector invalidation rust

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());
}
Run Code Online (Sandbox Code Playgroud)

Mut*_*pus 5

在 Rust 中,大多数方法都采用&self-对 self的引用。在大多数情况下,像some_string.len()内部这样的调用会“扩展”为这样的:

let a: String = "abc".to_string();
let a_len: usize = String::len(&a); // This is identical to calling `a.len()`.
Run Code Online (Sandbox Code Playgroud)

但是,请考虑对一个对象的引用:a_ref,它是一个&Stringthat 引用a。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.
Run Code Online (Sandbox Code Playgroud)

请注意,这基本上等同于原始示例,除了我们使用的是显式设置的引用a而不是a直接引用。

这意味着v.clone()扩展为Vec::clone(&v),类似地,v_ref.clone()扩展为Vec::clone(v_ref),并且由于v_ref&v(或者,具体地说,&mut v),我们可以将其简化为Vec::clone(&v)。换句话说,这些调用是等价的——调用对象clone()的基本引用 ( &) 不会克隆引用,而是克隆被引用的对象

换句话说,Tamas Hedgeus 的评论是正确的:您正在迭代一个新向量,其中包含的元素是 中元素的克隆v。在您的for循环中迭代的项目不是 a &Vec,它Vec是与 分开的v,因此迭代器失效不是问题。

至于你关于 Rust 提供的保证的问题,你会发现 Rust 的借用检查器可以很好地处理这个问题,没有任何附加条件。

但是,如果您要从循环中删除clone()for,则会收到一条错误消息,use of moved value: '*v_ref'因为当您对其进行迭代时,它v_ref被视为“已移动”到for循环中,并且不能用于该函数的其余部分;为避免这种情况,该iter函数创建了一个仅借用向量的迭代器对象,从而允许您在循环结束(并且迭代器被删除)后重用该向量。如果您尝试v没有v_ref抽象的情况下进行迭代和变异,错误将显示为cannot borrow 'v' as mutable because it is also borrowed as immutable. v在产生的迭代器中不可改变地借用v.iter()(它的类型签名为fn iter(&self) -> Iter<T>- 注意,它使借用到向量),并且不允许您由于 Rust 的借用检查器而改变向量,直到迭代器被删除(在for循环结束时)。但是,由于您可以拥有对单个对象的多个不可变引用,因此您仍然可以从 for 循环内的向量中读取,而不是写入其中。

如果您需要变异的元素的载体,同时遍历向量,就可以使用iter_mut,它返回可变同时引用一个元素,让你改变这种元素。你仍然不能用 来改变迭代向量本身iter_mut,因为 Rust 确保一次只有一个对对象的可变引用,并确保在与该对象的不可变引用相同的范围内没有对对象的可变引用。