2016-10-29 5 views
0

В процессе обучения Rust я знакомлюсь с распространением ошибок и выбором между unwrap и оператором ?. После написания кода прототипа, который использует только unwrap(), я хотел бы удалить unwrap из частей многократного использования, где паника на каждой ошибке неуместна.Альтернатива оператору try (?), Подходящему для отображения итератора

Как избежать использования unwrap в закрытии, как в этом примере?

// todo is VecDeque<PathBuf> 
let dir = fs::read_dir(&filename).unwrap(); 
todo.extend(dir.map(|dirent| dirent.unwrap().path())); 

Первый unwrap может быть легко изменен на ?, до тех пор, пока функция возвращает содержащий Result<(), io::Error> или аналогичный. Однако второй unwrap, тот, который находится в dirent.unwrap().path(), не может быть изменен на dirent?.path(), поскольку закрытие должно вернуть PathBuf, а не Result<PathBuf, io::Error>.

Одним из вариантов является изменение extend к явному цикла:

let dir = fs::read_dir(&filename)?; 
for dirent in dir { 
    todo.push_back(dirent?.path()); 
} 

Но что чувствует себя неправильно - оригинальный extend элегантна и четко отражено намерение кода. (Возможно, он был более эффективным, чем последовательность push_back.) Как опытный разработчик Rust выражает ошибку в таком коде?

ответ

3

Как избежать использования unwrap в закрытии, как в этом примере?

Ну, это действительно зависит от того, что вы хотите сделать при неудаче.

  • следует отказ сообщать пользователю или молчать
  • если сообщалось, должен один отказ сообщать или все?
  • если произошел сбой, должен ли он прерывать обработку?

Например, вы можете совершенно спокойно игнорировать все сбои и просто пропускать записи, которые не срабатывают. В этом случае Iterator::filter_map в сочетании с Result::ok - это именно то, о чем вы просите.

let dir = try!(fs::read_dir(&filename)); 
let todos.extend(dir.filter_map(Result::ok)); 

Интерфейс Iterator полон положительных героев, это определенно стоит просматривал при поиске опрятнее кода.

+0

Хорошие вопросы; Я не хотел упоминать конкретные примеры использования, чтобы избежать слишком долгого вопроса. В частности, я хотел бы сообщить все сбои и продолжить обработку. Например. рассмотрите функцию, которая подсчитывает файлы в подкаталогах ([python] (http://pastebin.com/XvzYZbsM)). [Ржавчина версия] (http://pastebin.com/HFKHq54T) паники при каждой ошибке, и я хотел бы, чтобы он регистрировал ошибку на stderr и продолжал подсчет, как это делает Python. – user4815162342

+0

@ user4815162342: В этом случае вам понадобится сопоставление шаблонов в цикле 'for' и добавление к коллекции' PathBuf' для обработки, а другое из 'Error' для проблем. –

+0

'filter_map (Результат: ok)' довольно аккуратно, спасибо. – user4815162342

1

Предложение представляет собой решение основано на filter_map, предложенном Matthieu. Он призывает Result::map_err обеспечить ошибка «поймали» и журнал, посылая его дальше Result::ok и filter_map, чтобы удалить его из итерации:

fn log_error(e: io::Error) { 
    writeln!(&mut std::io::stderr(), "{}", e).unwrap() 
} 

(|| { 
    let dir = fs::read_dir(&filename)?; 
    todo.extend(dir 
       .filter_map(|res| res.map_err(log_error).ok())) 
       .map(|dirent| dirent.path())); 
})().unwrap_or_else(log_error) 
Смежные вопросы