Как добавить ссылки на контейнер, когда заимствованные значения создаются после контейнера?
По причинам, связанным с организацией кода, мне нужно, чтобы компилятор принял следующий (упрощенный) код:
fn f() {
let mut vec = Vec::new();
let a = 0;
vec.push(&a);
let b = 0;
vec.push(&b);
// Use `vec`
}
Компилятор жалуется
error: `a` does not live long enough
--> src/main.rs:8:1
|
4 | vec.push(&a);
| - borrow occurs here
...
8 | }
| ^ `a` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error: `b` does not live long enough
--> src/main.rs:8:1
|
6 | vec.push(&b);
| - borrow occurs here
7 | // Use `vec`
8 | }
| ^ `b` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Тем не менее, мне трудно убедить компилятор удалить вектор перед ссылками на переменные. vec.clear()
не работает, и не работает drop(vec)
, mem::transmute()
тоже не работает (чтобы заставить vec
быть 'static
).
Единственное решение, которое я нашел, было преобразовать ссылку в &'static _
, Есть ли другой путь? Можно ли даже скомпилировать это в безопасном Rust?
1 ответ
Можно ли даже скомпилировать это в безопасном Rust?
Нет. То, что вы пытаетесь сделать, в общем случае небезопасно.
Коллекция содержит ссылку на переменную, которая будет удалена до удаления самой коллекции. Это означает, что деструктор коллекции имеет доступ к ссылкам, которые больше не действительны. Деструктор может выбрать разыменование одного из этих значений, нарушая гарантии безопасности памяти Rust.
примечание: значения в области видимости отбрасываются в обратном порядке их создания
Как говорит вам компилятор, вам нужно изменить порядок кода. Вы на самом деле не сказали, какие ограничения существуют по "причинам, связанным с организацией кода", но прямое решение таково:
fn f() {
let a = 0;
let b = 0;
let mut vec = Vec::new();
vec.push(&a);
vec.push(&b);
}
Менее очевидный:
fn f() {
let a;
let b;
let mut vec = Vec::new();
a = 0;
vec.push(&a);
b = 0;
vec.push(&b);
}
При этом, как только нелексические времена жизни будут включены, ваш оригинальный код будет работать! Средство проверки заимствований становится более детализированным о том, как долго нужно жить стоимости.
Но подожди; Я только что сказал, что коллекция может получить доступ к недопустимой памяти, если значение внутри нее будет удалено перед коллекцией, и теперь компилятор позволяет этому случиться? Что дает?
Это потому, что стандартная библиотека тянет нас на хитрость. Коллекции как Vec
или же HashSet
гарантировать, что они не имеют доступа к своим общим параметрам в деструкторе. Они сообщают об этом компилятору, используя нестабильный#[may_dangle]
особенность.
Смотрите также: