Как мне хранить коллекцию одной структуры в другой, где время жизни не предсказуемо?

Я хочу управлять коллекцией объектов в другом объекте, но не могу предсказать время жизни элементов в этой коллекции.

Я нашел этот пример в спецификаторе жизни синтаксиса Rust, который демонстрирует, что я не могу сделать:

struct User<'a> {
    name: &'a str,
}

// ... impls omitted

struct ChatRoom<'a> {
    name: &'a str,
    users: HashMap<&'a str, User<'a>>,
}

ChatRoom держит карту Users. каждый 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>,
}
Другие вопросы по тегам