Доступ к вложенным структурам без перемещения

#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 of unwrap , но в какой -то момент вам нужно либо утверждать, что опция является некоторой, либо разумно обработать ошибку.

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. Спасибо за объяснение. Мне очень помогло понять проблему, с которой я столкнулся!