Matching значением движется значения в переменных шаблона. Перемещение делает исходное значение непригодным для использования, за исключением очень простых объектов, которые реализуют черту Copy
, например числа. В отличие от указателей в C, изменяемые ссылки не являются копируемыми, которые можно увидеть в следующем примере, который не компилируется либо:
let mut v = vec![1, 2, 3];
let rv = &mut v; // mutable reference to v
{
// move rv to r1, r1 now becomes the sole mutable reference to v
let r1 = rv;
r1.push(4);
}
{
let r2 = rv; // error: rv was already moved to r1
r2.push(5);
}
Ржавчина отклоняет выше, потому что он усиливает общее правило, запрещающее множество изменяемых ссылок на объект , Несмотря на то, что этот конкретный фрагмент является безопасным, одновременное использование нескольких изменяемых ссылок на один и тот же объект упростит запись типа небезопасных программ, которые Rust явно предназначен для предотвращения, например, те, которые содержат расы данных в многопоточном коде или те, которые обращаются к данным через недействительный итератор. Таким образом, назначение let r1 = rv
может только переместитьrv
ссылка на r1
, запрещая инструкцию let r2 = rv
, которая теперь относится к перемещенной переменной rv
.
Чтобы исправить код, мы должны создать отдельные ссылки к ссылке. Это создаст две различных изменяемые ссылки на исходную ссылку, а не перемещение исходной изменяемую ссылки на внутреннюю сферу:
let mut v = vec![1, 2, 3];
let mut rv = &mut v;
{
// rr1 is a *new* mutable reference to rv - no move is performed
let rr1 = &mut rv;
rr1.push(4);
}
{
// rr2 is a *separate* new mutable reference to rv - also no move
let rr2 = &mut rv;
rr2.push(5);
}
Синтаксис push
вызова является то же самое с r1.push
и rr1.push
, потому что .
оператор Русте автоматически разыменовать любого количество ссылок.
Для возврата к примеру от вопроса, ссылка на Vec<usize>
в Option
подобна v
ссылки выше, и сопоставления его с помощью шаблона Some(v)
перемещает ссылку в переменном с v
узора.
Чтобы исправить это, шаблон должен быть изменен, как в приведенном выше примере, чтобы указать переменную, которая относится к к значению, которое само по себе является ссылкой. Это достигается с помощью синтаксиса if let Some(ref mut v)
. Как и выше, если объявление rv
должно быть изменено на mutable, это исправление также требует изменения переменной Option
. С учетом этих двух изменений, код компилируется:
fn maybe_push(mut v_option: Option<&mut Vec<usize>>) -> usize {
let mut c = 0;
if let Some(ref mut v) = v_option {
for i in 0..10 {
v.push(i);
c += i;
}
} else {
for i in 0..10 {
c += i;
}
}
if let Some(ref mut v) = v_option {
for i in 10..20 {
v.push(i);
c += i;
}
} else {
for i in 10..20 {
c += i;
}
}
return c;
}
fn main() {
let mut v: Vec<usize> = vec![];
println!("{}", maybe_push(Some(&mut v)));
println!("{}", maybe_push(None));
println!("{:?}", v);
}
Другая возможность заключается в том, чтобы использовать метод as_mut()
вернуть содержимое опции как изменяемые ссылки на предыдущий контент. Это концептуально эквивалентно изменению от let r1 = rv
до let rr1 = &mut rv
в первом фрагменте и позволит использовать шаблон if let Some(v)
, где v
все равно будет ссылкой на изменяемую ссылку на вектор. Это также требует, чтобы v_option
был объявлен изменчивым.
В книге: https://doc.rust-lang.org/book/patterns.html#ref-and-ref-mut (очень очень кратким). –
@Shepmaster Хорошая альтернатива, спасибо. Я теперь обновил ответ, чтобы включить его, а также дополнительное объяснение, почему исходный ход не работает. – user4815162342