Защищает ли меня 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 гарантирует, что существует только одна изменяемая ссылка на объект за раз, а также гарантирует, что нет изменяемых ссылок на объект в той же области видимости, что и неизменяемые ссылки на этот объект.