Pull to refresh

Comments 15

Anyhow и thiserror это произведения исскусства, которые делают нелегкую жизнь разработчика на расте намного лучше :)

  • Всегда есть способ определить ответственность, кто «виновен» в панике. Либо это вина функции, которая запаниковала, либо вина вызывающей стороны.

Либо ОС вернула код ошибки 2 и никто не знает почему. То ли это код кривой, то ли вызов.

Вот это для меня основная трудность, понять откуда пришла ошибка.

Может быть я не прав, но я избалован исключениями (Java, JavaScript), поэтому когда в Rust’е я пользуюсь фреймворком, который за меня возвращает Result с ошибкой, и я пытаюсь её привести к значению, то меня это постоянно раздражает. Я не вижу источника ошибки (чтобы почитать чужой код и понять причину), а вижу лишь место своей паники.

А я наоборот, никогда не использую expect(). Что-то эта функция ни в царский жезл, ни в красную армию. unwrap() я использую тогда, когда у меня объект завернут, но я уже точно знаю, что он есть. unwrap() что-то навроде assert(), отличается невыразимой армейской краткостью. Если же есть хоть малейшие сомнения, что объект там есть, то уже надо использовать разнообразные unwrap_or или map.

Поэтому expect() странен: мы заявляем, что совершенно уверены, что он никогда не сработает, и тут же пишем ему аргумент на случай, если он сработает. При том, что если сработает unwrap(), то разработчик всё равно получит всю инфу о проблеме, а зачем рассказывать юзеру технические детали большого облома, неясно. В отчёте от него вам всё равно будет указана строка в коде, unwrap() её выводит.

По этой причине, никогда не пишите два unwrap() на одной строке.

Важно избегать такого кода:

let cthulhu: Option<Cthulhu> = call_cthulhu();
if cthulhu.is_none() { 
    return;
}
// Много печальных приготовлений на 3 страницы
tickle(cthulhu.unwrap());

Сейчас с этим кодом всё в порядке. Но проверка option никак не связана с его unwrap() и в дальнейшем может или условие потеряться, или вторая половина кода куда-то переехать. Из мира С++ до нас доносятся стоны, что количество вложенных уровней кода надо сокращать, именно тем способом, что у меня в примере. Не надо только жертвовать надёжностью кода ради этого. Используйте if let Some(cthul) == cthulhu {}

Как один из упоротых вариантов: можно распаковать перед использованием, и потом с распакованным значением работать. Но выглядит как-то так себе:

let cthulhu = if let Some(cthulhu) = call_cthulhu() {
    cthulhu
} else {
    return;
};
// ...
tickle(cthulhu);

В таких случаях очень хорошо бы использовать ?, но это не всегда возможно

let cthulhu = match (call_cthulhu()) {
    Some(c) => c,
    _ => return,
};

Да, такой вариант уже приятнее глазу, но он всё еще не сравнится с оператором ?. А для него необходимо иметь возвращаемое значение вида как минимум Option<()> что воспринимается как: можем вернуть ничего либо ничего. Хотя мне кажется здесь лучше вернуть ошибку, если Ктулху не пришел и ее пробросить выше, но нужно смотреть на контекст.

Для просто для краткости. Вот прямо сегодня на работе кодил метод "получить рамку слева от текущей". Неполучиться рамка может только если её нет - ну и зачем мне возвращать ошибку, если и так всё понятно? У меня такие ситуации возникают часто, когда функцию планируется вызывать в цикле до тех пор, пока она не сломается. Как в том анекдоте про пилу. И тогда совсем не важно, почему она сломалась: результат достигнут, список рамок отсюда до левого края составлен, зачем мне суть ошибки? А, ну и ещё я на С++ пишу на работе, там Option есть, а Result нет, а колхозить не хочу.

А для него необходимо иметь возвращаемое значение вида как минимум Option<()>

Но ведь ничто не мешает в, условно, одну строчку конвертироваться из Option<T> в Result<T, E> и далее сразу его вернуть try-оператором.

fn try_operator_variant() -> Result<String, &'static str> {
    let cthulhu = call_cthulhu()
        .ok_or("Failed to call cthulhu")?;

    tickle(cthulhu);

    todo!("Don't forget about me")
}

Учитывя что у вас сигнатуры возвращаемых данных расходятся, то оно даже не соберётся. С распакованным значением в таком случае надо работать внутри же условия.

у вас сигнатуры возвращаемых данных расходятся

Почему же? Оба варианта правильные.

В случае с if-let-else ещё происходит shadowing предыдущей локальной переменной cthulhu: Option<Cthulhu> новой cthulhu: Cthulhu, полученной в качестве результата if-let-else выражения.

Ну и, как верно подметил выше @medvedevia, с недавних пор есть компактный let-else:

let Some(cthulhu) = call_cthulhu() else {
    return;
};

// ...
tickle(cthulhu);

количество вложенных уровней кода надо сокращать

Для этого можно использовать макрос ward!

let cthulhu = ward!(cthulhu, return);
Sign up to leave a comment.

Articles