Разберите следующий код на Rust: let v = vec![1,2,3]; let r = &v[0..2]; v.push(4); println!("{:?}", r); Почему этот код не компилируется или вызывает ошибку во время выполнения, и как правильно управлять заимствованиями?
Коротко: проблема в правилах заимствований и возможной реколокации буфера вектора. Почему не компилируется / даёт ошибку: - В вашем коде сначала берётся срез-неизменяемое заимствование: `let r = &v[0..20..20..2];`. Это создаёт immutable borrow вектора `v`. - Метод `push` требует изменяемого заимствования `&mut self`. Нельзя одновременно иметь immutable borrows и брать `&mut` — компилятор выдаст ошибку вроде «cannot borrow `v` as mutable because it is also borrowed as immutable». - Даже если бы borrow checker этого не запретил (например, используя unsafe или raw pointers), `v.push(444)` может вызвать реколокацию внутреннего буфера, и тогда срез `r` станет висячей ссылкой (use-after-free) — UB. Правильные варианты управления заимствованиями: 1) Выполнить `push` до того, как взять срез: let mut v = vec![1, 2, 31,\ 2,\ 31,2,3]; v.push(444); let r = &v[0..20..20..2]; 2) Ограничить область жизни неизменяемого заимствования (вложенным блоком), чтобы оно было отпущено до `push`: let mut v = vec![1, 2, 31,\ 2,\ 31,2,3]; { let r = &v[0..20..20..2]; println!("{:?}", r); } // r здесь выходит из области видимости v.push(444); 3) Клонировать срез, если нужно сохранить данные независимо от вектора: let mut v = vec![1, 2, 31,\ 2,\ 31,2,3]; let r = v[0..20..20..2].to_vec(); // копия v.push(444); println!("{:?}", r); 4) В особых случаях можно использовать unsafe/raw указатели, но это рисковано и обычно не нужно. Итог: либо не держите immutable borrow в тот момент, когда нужно мутировать `v`, либо сделайте копию данных.
Почему не компилируется / даёт ошибку:
- В вашем коде сначала берётся срез-неизменяемое заимствование: `let r = &v[0..20..20..2];`.
Это создаёт immutable borrow вектора `v`.
- Метод `push` требует изменяемого заимствования `&mut self`. Нельзя одновременно иметь immutable borrows и брать `&mut` — компилятор выдаст ошибку вроде «cannot borrow `v` as mutable because it is also borrowed as immutable».
- Даже если бы borrow checker этого не запретил (например, используя unsafe или raw pointers), `v.push(444)` может вызвать реколокацию внутреннего буфера, и тогда срез `r` станет висячей ссылкой (use-after-free) — UB.
Правильные варианты управления заимствованиями:
1) Выполнить `push` до того, как взять срез:
let mut v = vec![1, 2, 31,\ 2,\ 31, 2, 3];
v.push(444);
let r = &v[0..20..20..2];
2) Ограничить область жизни неизменяемого заимствования (вложенным блоком), чтобы оно было отпущено до `push`:
let mut v = vec![1, 2, 31,\ 2,\ 31, 2, 3];
{
let r = &v[0..20..20..2];
println!("{:?}", r);
} // r здесь выходит из области видимости
v.push(444);
3) Клонировать срез, если нужно сохранить данные независимо от вектора:
let mut v = vec![1, 2, 31,\ 2,\ 31, 2, 3];
let r = v[0..20..20..2].to_vec(); // копия
v.push(444);
println!("{:?}", r);
4) В особых случаях можно использовать unsafe/raw указатели, но это рисковано и обычно не нужно.
Итог: либо не держите immutable borrow в тот момент, когда нужно мутировать `v`, либо сделайте копию данных.