Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
a f b (метод), а не f a b, свойствам, функциям высшего порядка, обобщенным классам, делегатам и, наконец, исключениям. Отказ от этого — это откат на 20 лет назад.Я привык к нормальным исключениям, дополнительной информации в них, стек трейсам и общему базовому классу (причём не только для исключений).
Ещё я пишу на С++. Там с исключениями похуже, но всё же терпимо. Именно С++ я хочу заменить на Rust. Отсутствие исключений меня останавливает в первую очередь.
div. Давайте рассмотрим варианты обработки этой ошибки.Без обработки ошибок, но с одним throw именно в том месте, где может быть ошибка — в div. А может и без throw, пусть язык этим занимается.
На вершине стека вы получаете полный стек трейс, сообщение об ошибке и какие-то дополнительные данные, ничего для этого не сделав.
Оборачивание исключений в этих 50 функциях в нужных местах.
Получаете production-ready код с морем дебаг информации, обернув в нужном месте исключения.
На вершине стека вы в лучем случае получаете DivisionByZero. Полностью бесполезный, у вас там снизу 50 функций.Там есть возможность с помощью макросов
file! и line! получить номер строки в файле. Просто напишите свой try!, который будет не просто возвращать ошибку, а ещё и добавлять к ней информацию о том, где она пробрасывалась. Это вариант 3, но «кучу кода» пишет компилятор. Эти же макросы могут быть использованы и в месте генерации ошибки.panic!). Если это основной таск, то приложение закрывается.Это заставит не использовать исключения в рабочих сценариях — только если на самом деле произошла невосстановимая ошибка и надо перейти к следующему элементу, залогировав ошибку.Во-первых, восстановиться после panic нельзя, только из другого таска. Во-вторых, не всегда можно отдать код в отдельный таск, есть требовательные к, например, производительности места. В-третьих, писать код с вездесущим try и проверкой на 0 перед делением — слишком дорого. Выше я написал комментарий с возможными сценариями.
Если это основной таск, то приложение закрывается.
// This is a three level match pyramid!
match checked::div(x, y) {
Err(why) => fail!("{}", why),
Ok(ratio) => match checked::ln(ratio) {
Err(why) => fail!("{}", why),
Ok(ln) => match checked::sqrt(ln) {
Err(why) => fail!("{}", why),
Ok(sqrt) => sqrt,
},
},
} они будто бы даже гордятся тем, что можно городить пирамидку из вызовов и проверок их Result на ошибки.checked::div(x, y) >>= |ratio| checked::ln(ratio) >>= |ln| checked::sqrt(ln)let ratio = checked::div(x, y).unwrap();
let ln = checked::ln(ratio).unwrap();
let sqrt = checked::sqrt(ln).unwrap();checked::div(x, y).and_then(|ratio| checked::ln(ratio).and_then(|ln| checked::sqrt(ln).unwrap()))try {
foo();
bar();
baz();
} catch (Error) {
...
}
Chaining results using match can get pretty untidy; luckily, the try! macro can be used to make things pretty again. The try! macro expands to a match expression, where the Err(err) branch expands to an early return Err(err), and the Ok(ok) branch expands to an ok expression.
let sqrt = try!(checked::sqrt(x));
do_smth(sqrt);
match checked::sqrt(x) {
Err(why) => return Err(why),
Ok(sqrt) => {
do_smth(sqrt);
}
}
try! разворачивается в зависимости от аргумента либо в return why из Err(why), либо в value из Ok(value). Этот самый value потом можно чему-то присвоить: let sqrt = try!(checked::sqrt(x)); и уже после этого над ним изобразить do_smth(sqrt);.try! определен следующим образом:macro_rules! try(
($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
)
let sqrt = try!(checked::sqrt(x));
do_smth(sqrt);
let sqrt = match checked::sqrt(x) { Ok(e) => e, Err(e) => return Err(e) };
do_smth(sqrt);
Rust'а не могу, извиняйте.rust»), а также сами создатели языка (оранжевые бейджи).Result и Option. Кроме того, в коммьюнити языка одна из наиболее желаемых фич — это higher-kinded types, которые открывают дорогу, в том числе, к обобщённым монадам и обобщённым операторам для работы с ними, наподобие do-нотации хаскелля или for comprehensions скалы. К сожалению, в 1.0 этого не будет просто по причине нехватки времени.Go имеет свою конкретную, вполне узкую область — это язык для написания бэкендовОдин брякнул и все повторяют из уст в уста, что значит для бекэндов? Нет, я не спорю, что в realtime задачах при ограниченном наборе ресурсов или при большом количестве данных требуется специализированный подход, но Go привязывать к «backend» смысла нет. Вот я бы поспорил, удобнее ли на Rust писать GUI программы, чем на Go.
fn get_iter(&self) -> Iterator<i64>;.panic!(). Да, такие «исключения» нельзя ловить, но это трудно сделать кроссплатформенно и чревато небезопасностью работы с памятью, поэтому в Rust так делать нельзя.Если результатом функции может оказаться ошибка, то это видно из её сигнатуры, код становиться более явным.
В Rust 1.0 исключений не будет