Защищает ли меня 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());
}

1 ответ

Решение

В Rust большинство методов &self - ссылка на себя. В большинстве случаев звонок как some_string.len() внутренне "расширяется" до чего-то вроде этого:

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

Однако рассмотрим ссылку на объект: a_ref, который является &String что ссылки 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.

Обратите внимание, что это в основном эквивалентно исходному примеру, за исключением того, что мы используем явно установленную ссылку на a скорее, чем a непосредственно.

Это означает, что v.clone() расширяется до Vec::clone(&v) и так же, v_ref.clone() расширяется до Vec::clone(v_ref), и с тех пор v_ref является &v (или, в частности, &mut v), мы можем упростить это обратно в Vec::clone(&v), Другими словами, эти вызовы эквивалентны - вызов clone() по основной ссылке (&) к объекту не клонирует ссылку, он клонирует ссылочный объект.

Другими словами, комментарий Тамаса Хеджеуса ​​верен: вы перебираете новый вектор, который содержит элементы, которые являются клонами элементов в v, Элемент перебирается в вашем for петля не является &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 гарантирует, что существует только одна изменяемая ссылка на объект за раз, а также гарантирует, что нет изменяемых ссылок на объект в той же области видимости, что и неизменяемые ссылки на этот объект.

Другие вопросы по тегам