#rust #borrow-checker
Вопрос:
Я пытаюсь реализовать структуру графика, имея в каждом узле ссылки на его соседей. В частности, я пытаюсь создать сетку, в которой каждый узел содержит ссылки до 4 соседей — например, «2D-связанный список».
Но я получаю ошибку при назначении ссылок. Этот минималистичный пример воспроизводит мою проблему:
#[derive(Clone)]
struct Node<'a> {
neighbor: Option<amp;'a Node<'a>>, // optional reference to another Node
}
fn main() {
// a bunch of nodes:
let mut nodes: Vec<Node> = vec![ Node{neighbor: None}; 100];
// I want node 0 to have a reference to node 1
nodes[0].neighbor = Some(amp;nodes[1]);
}
Возникает следующая ошибка:
error[E0502]: cannot borrow `nodes` as mutable because it is also borrowed as immutable
--> src/main.rs:12:5
|
12 | nodes[0].neighbor = Some(amp;nodes[1]);
| ^^^^^------------------------------
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| immutable borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
Я изо всех сил пытаюсь понять, как это должно быть сделано в Rust. Должен ли я вместо этого использовать указатели?
Комментарии:
1. почему бы вам не хранить индексы вместо ссылок на сами узлы?
2. Я надеялся, что смогу сделать оставшуюся часть моей программы независимой от этой структуры, которая использовалась для создания экземпляров узлов. Но, может быть, это плохая идея в Rust?
3. вы определенно можете так поступить, просто индексы тоже просты. Возможно, вы захотите взглянуть на
Rc
это .4. Возможно , GhostCell мог бы вам помочь, он создан именно для этой цели. Вот репозиторий github с некоторыми коллекциями, использующими его , и вот сообщение reddit о 1d связанном списке
5. … и если ни один из безопасных подходов не работает для вас (эргономика, производительность, что угодно), вы всегда можете отказаться от небезопасных блоков и необработанных указателей
Ответ №1:
Вы не можете реализовать такую структуру данных именно по той причине, по которой была создана проверка заимствований. Это говорит вам о том, что ваша структура данных не может быть безопасной.
Причина, по которой это небезопасно, довольно проста.
Предположим, вы создали график с двумя элементами, и первый из них ссылается на второй. Теперь вы хотите, чтобы второй вариант ссылался на первый. Чтобы изменить свой график, вам потребуется получить изменяемый доступ к структуре. Но если вы получите изменяемый доступ к нему, ничто не сможет помешать вам удалить второй узел из графика, сделав ссылку в первом узле недействительной.
Таким образом, проверка заимствования не позволит вам этого сделать.
Для вас лучше всего использовать индексы вместо ссылок. Это будет иметь дополнительное преимущество: когда вы захотите сериализовать/десериализовать свою структуру, у вас не будет никаких проблем с этим.
Комментарии:
1. Это можно сделать с помощью узлов, выделенных в куче, с помощью
Rc/Weak
2. Спасибо, теперь я понимаю, почему это запрещено, и в отличие от того, как я изначально думал, это не недостаток в компиляторе
3. … однако в гипотетическом сценарии, когда узлы могут быть удалены, я должен задаться вопросом, какую пользу приносит реализация индекса. Это «безопасно» с точки зрения компилятора, конечно, но индексы становятся недействительными, как и ссылки.
4. Если вы обманете компилятор (например, используя unsafe) и заставите его разрешить вам использовать ссылки и случайно разыменовать недопустимую ссылку, у вас будет нарушение доступа к памяти. Это гораздо худшая проблема, и ее гораздо сложнее отлаживать, чем паниковать в случае использования неправильного индекса без проверки.