🌌 Тиха обробка пасток — С.І.Д. залюбки розкладає дві категорії проблем, які ніколи не плутає: «пастку», яку можна обробити й піти далі (Result), і «зраду власного коду» — порушення внутрішнього інваріанта, коли продовжувати роботу безглуздо (panic!). І тепло наполягає: читання чужих, потенційно битих логів персоналу завжди належить до першої категорії.
Rust навмисно не має винятків (exceptions) у стилі Java/Python. Замість
цього він розрізняє дві принципово різні категорії проблем часу
виконання. Відновні помилки (recoverable errors) — це очікувані,
передбачувані відхилення від щасливого шляху: файл не знайдено,
мережа недоступна, вхідні дані мають неправильний формат. Такі помилки
представлені типом Result<T, E> — enum із варіантами Ok(T) (успіх зі
значенням) і Err(E) (провал із описом причини). Непоправні помилки
(unrecoverable errors) — це порушення внутрішнього інваріанта програми,
ситуація, в якій продовження виконання небезпечне або безглузде: вихід
за межі масиву, поділ на нуль, порушений контракт функції. Для них
призначений panic! — макрос, що негайно розкручує стек (unwind) і
завершує потік (за замовчуванням) або весь процес (у режимі panic = "abort").
Ключовий критерій вибору між ними — чи очікує викликач цю ситуацію
і чи здатний він осмислено на неї відреагувати. Читання файлу
журналу, наданого зовнішнім джерелом (можливо, пошкодженого чи не в
тому кодуванні), — типовий кандидат на Result: викликач цілком
може вирішити пропустити цей файл, повторити спробу чи повідомити
користувача — програма не повинна аварійно завершуватися через один
битий вхідний файл серед тисячі. А от, наприклад, звернення до
елемента вектора за індексом, обчисленим самою програмою на основі
внутрішньої логіки, яка гарантує коректність (vec[computed_index],
де computed_index за побудовою алгоритму не може вийти за межі), —
якщо це припущення все ж порушується, це свідчить про баг у самій
програмі, а не про очікувану зовнішню обставину; тут доречний
panic! (чи явний .expect("інваріант X порушено: ...")) — продовжувати
виконання з хибним внутрішнім станом небезпечніше, ніж зупинитися.
Result<T, E> компонується через оператор ? — якщо функція сама
повертає Result, вираз some_call()? автоматично «розпаковує»
Ok-значення або негайно повертає Err із поточної функції (за
потреби конвертуючи тип помилки через трейт From). Це дозволяє
писати послідовність операцій, кожна з яких може провалитися, без
ланцюжків вкладених match, зберігаючи водночас повну явність: сама
сигнатура функції (-> Result<T, E>) чесно повідомляє виклику, що
вона може провалитися, і компілятор змушує це враховувати (ігнорування
Result без обробки дає попередження unused_must_use).
Практичні маркери в коді, на які варто зважати: методи .unwrap() і
.expect(msg) над Result/Option — це навмисний вибір «перетворити
відновну помилку на panic тут і зараз», доречний у прототипах, тестах
чи там, де провал справді означає баг програми, а не зовнішню обставину.
У виробничому коді, що працює з ненадійним зовнішнім входом (файли,
мережа, ввід користувача), масове використання .unwrap() — типова
ознака недбалої обробки помилок: перша ж «побита» вхідна умова
призведе до аварійного завершення всієї програми там, де достатньо
було б обробити один файл як помилковий і продовжити роботу з рештою.
Важливо й те, що panic! у Rust — не те саме, що виняток у Java:
за замовчуванням стандартна бібліотека не пропонує «зловити» panic і
продовжити виконання як штатний контроль потоку (хоча технічно
std::panic::catch_unwind існує, він призначений для меж системи —
наприклад, щоб один запит у веб-сервері не «поклав» увесь процес — а
не для звичайної обробки помилок бізнес-логіки, для якої й існує
Result).
Прочитати розділи «Recoverable Errors with Result» та «To panic! or Not to panic!» у The Rust Book; знайти в стандартній бібліотеці приклад функції, що повертає Result, і приклад функції, що panics при порушеному інваріанті.
Наведіть приклад ситуації, де panic! доречний, і приклад, де для тієї самої категорії збою правильніший вибір — Result.
cargo run -- --index 3101 — отримаєш index out of bounds: the len is 10 but the index is 3101, а з --safe — спокійний None. С.І.Д. каже, що 3101 — єдиний індекс, на якому він сам ніколи не панікує: за цією адресою в нього завжди щось лежить.Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.
cargo run-- йдуть програмі (cargo run -- staff.log).cargo💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.