Как мне хранить коллекцию одной структуры в другой, где время жизни не предсказуемо?
Я хочу управлять коллекцией объектов в другом объекте, но не могу предсказать время жизни элементов в этой коллекции.
Я нашел этот пример в спецификаторе жизни синтаксиса Rust, который демонстрирует, что я не могу сделать:
struct User<'a> {
name: &'a str,
}
// ... impls omitted
struct ChatRoom<'a> {
name: &'a str,
users: HashMap<&'a str, User<'a>>,
}
ChatRoom
держит карту User
s. каждый User
это копия, хотя имя в User
является общей ссылкой. User
и ChatRoom
имеют явное время жизни, поэтому, когда они объединены, компилятор обеспечивает User
должны жить дольше, чем ChatRoom
они собираются в.
Но что если мой User
был создан после ChatRoom
? Я не могу использовать время жизни, потому что компилятор будет жаловаться. Что если я удалю User
перед ChatRoom
? Я тоже не могу этого сделать.
Как можно ChatRoom
держать User
Кто может быть создан после этого или разрушен до него? Я смутно подозреваю, что с коробками можно что-то сделать, чтобы реализовать это, но документация для коробок в Rust довольно скудная, поэтому я не уверен.
1 ответ
В Rust некоторые типы бывают попарно: заимствованные и находящиеся в собственности коллеги. Для строк заимствованная версия &'a str
и собственная версия String
, Принадлежащие версии не имеют параметра времени жизни, потому что они владеют всеми своими данными. Это не значит, что они не содержат указателей внутри; String
хранит свои данные в куче, а фактические String
Объект содержит только указатель на эти данные.
Используя String
вместо &'a str
вы можете избежать проблем с порядком построения, потому что вы можете свободно перемещать собственные данные (если они не заимствованы где-либо еще). Например, когда вы создаете User
сначала нужно создать String
, который вы бы затем перейти в новый User
и, наконец, вы затем двигаете User
в ChatRoom
"s HashMap
,
struct User {
name: String,
}
struct ChatRoom {
name: String,
users: HashMap<String, User>,
}
Однако, так как вам нужны общие ссылки, то вам нужно обернуть String
в типе, который обеспечивает эту функциональность. Если вы пишете однопоточную программу, вы можете использовать Rc
за это. Если вам нужно получить доступ к этим ссылкам из нескольких потоков, то Rc
не сработает; тебе нужно Arc
вместо.
struct User {
name: Rc<String>,
}
struct ChatRoom {
name: String,
users: HashMap<String, User>,
}
Для того, чтобы создать новый Rc<String>
которая указывает на ту же строку, вы просто вызываете clone()
метод на первом Rc<String>
,
Теперь String
в Rc<String>
неизменен, потому что Rc
не предоставляет никакого способа изменить его значение (без использования Rc
). Если вам нужна эта возможность, то вам нужно соединить это с RefCell
(или другое Mutex
или же RwLock
в многопоточной программе).
struct User {
name: Rc<RefCell<String>>,
}
struct ChatRoom {
name: String,
users: HashMap<String, User>,
}