#nested #rust
#вложенный #Ржавчина
Вопрос:
У меня есть эти структуры:
#[derive(Debug, RustcDecodable)]
struct Config {
ssl: Option<SslConfig>,
}
#[derive(Debug, RustcDecodable)]
struct SslConfig {
key: Option<String>,
cert: Option<String>,
}
Они заполняются из toml
файла. Это работает отлично. Поскольку я получил Option<T>
в нем, я должен либо вызвать unwrap()
, либо выполнить match
.
Но если я хочу сделать следующее:
let cfg: Config = read_config(); // Reads a File, parses it and returns the Config-Struct
let keypath = cfg.ssl.unwrap().key.unwrap();
let certpath = cfg.ssl.unwrap().cert.unwrap();
Это не сработает, потому cfg.ssl
что перемещается в keypath
. Но почему он перемещается? Я unwrap()
призываю ssl
, чтобы получить ключ (и unwrap()
его). Итак, результат key.unwrap()
должен быть перемещен?
Или я что-то упускаю? Каков наилучший способ сделать эти структуры доступными таким образом (или другим аккуратным способом)? Я пытался реализовать #[derive(Debug, RustcDecodable, Copy, Clone)]
, но это не сработает, потому что я Copy
также должен реализовать String
to. Затем я должен реализовать Copy
to Vec<u8>
и так далее. Должно быть более удобное решение?
Ответ №1:
Что такое определение Option::unwrap
? Из документации:
fn unwrap(self) -> T
он использует свои входные данные ( cfg.ssl
здесь ).
Это не то, что вы хотите, вместо этого вы хотите перейти от Option<T>
к amp;T
, который начнется с потребления amp;self
(по ссылке, а не по значению)… или вы хотите, чтобы clone
Option
перед вызовом unwrap
.
Клонирование редко является решением… альтернативой здесь является as_ref
:
fn as_ref(amp;self) -> Option<amp;T>
И поэтому вы можете написать:
let keypath /*: amp;String*/ = cfg.ssl.as_ref().unwrap().key.as_ref().unwrap();
^~~~~~~ ^~~~~~~~
Комментарии:
1.
cfg.ssl.as_ref().map(|ssl| amp;ssl.key).unwrap()
короче… Однако я не уверен, что лучше написать здесь.2. @LukasKalbertodt: выглядит немного сложно … хотя, конечно, проблема здесь
unwrapping
невольно.3. Возможно, вы могли бы избежать первого развертывания с
as_ref().and_then()
помощью instead ofunwrap
, но в какой -то момент вам нужно либо утверждать, что опция является некоторой, либо разумно обработать ошибку.4. @Jsor Спасибо, что указали на это: конечно, мой фрагмент кода неверен. Вместо этого должно быть указано
and_then
map
…5. После того, как я прочитал это и многие другие объяснения, мне потребовалось несколько дней, чтобы понять суть: доступ к вложенным полям в порядке. Но во время этого может произойти перемещение из поля. В данном случае, потому
unwrap()
что иmap()
потребляютself
; в моем случае даже перемещениеOption<amp;SomeType>
все равно было перемещением, потому что оно перемещалосьOption<>
.
Ответ №2:
Итак, результат
key.unwrap()
должен быть перемещен?
Да, но не только это. Ключевым моментом здесь является то, что переменная get перемещается в of unwrap()
. Давайте посмотрим на сигнатуру функции:
fn unwrap(self) -> T { ... }
Это занимает self
, поэтому объект перемещается в функцию. Но это относится и к ssl.unwrap()
, тоже!
Поэтому при записи:
cfg.ssl.unwrap().key.unwrap();
Сначала вы перемещаетесь cfg.ssl
unwrap()
, затем получаете доступ к одному полю результата и снова перемещаете это поле unwrap()
. Так что да, cfg.ssl
перемещается. Чтобы решить эту проблему, вы можете сохранить временный результат первого unwrap()
вызова, например:
let ssl = cfg.ssl.unwrap();
let keypath = ssl.key.unwrap();
let certpath = ssl.cert.unwrap();
Или вы можете посмотреть на as_ref()
метод, если вы не хотите перемещаться (что, вероятно, так и есть).
Комментарии:
1. Спасибо за объяснение. Мне очень помогло понять проблему, с которой я столкнулся!