4 3 Ошибки и исключения

Множественный возврат

Мы можем вернуть из функции несколько значений. Для этого мы возвращаем кортеж.

fn func() -> (let a,b)
{
    return (10, "test");
}

let (a,b) = func();
print(a) // 10
print(b) // "test"

Возможность бросать исключения

Возможность выбросить исключения должна быть описана явно если функция бросает исключения.

import exc::wrong_arg;

fn deiv(let a : num, let b : num) -> throws(exc::wrong_arg), (let num)
{
    if (b == 0)
        throw exc::wrong_arg("Wrong arg, b == 0");
    return a / b;
}

В скобках throws перечислены возможные виды исключений. Если оставить поле пустым - функция возвращает любое значение. Если написать throws(false) функция гарантирует что не вернёт исключения. В таком случае мы должны внутри неё не вызывать функций которые могут вернуть исключения или обрабатывать их.

Исключения тоже можно проверять на корректность как и возвращаемое значение.

fn test() -> throws({@.line == 10})
{

}

Ошибка могла произойти в строке 10 и только там. Если во время проверки исключения мы получили ошибку, будет выброшено ещё одно исключение.

Выброс ошибки/исключения

fn func() -> throw::auto
{
    if (int(input()) > 10)
        throw (exc::overflow, "more than 10");
    else
        return "less than 10";
}

Мы запрашиваем ввод. Если введено число больше 10 то выбрасывается исключение type::ex::overflow и к нему прикрепляется результат функции "more than 10" Иначе мы возвращаемся из функции штатно и возвращаем "less than 10"

Возврат ошибки как в С

fn nodiscard func()
{
    // ...
    return exc::overflow;
}

В данном случае мы возвращаем ошибку через значение функции. Мы можем поймать такую ошибку как и через оператор throw Спецификатор nodiscard говорит, что мы не можем проигнорировать возвращаемое значение.

Однострочный синтаксис

let a : type::res = try func();
match(a.ex)
{
    ex::ok => { print("Ok"); print(a.res); }
    ex::overflow => { print("Overflow"); }
    default => { throw; }
}

Мы пробуем вызвать функцию func и получаем в объект типа type::res. У этого объекта есть 2 поля - res и ex. Поле res хранит то, что вернула функция, а поле ex исключение или код ошибки. Если мы дошли до ex::default мы не отловили никаким из предыдущих пунктов ошибки. Мы можем переслать исключение дальше.

Многострочный синтаксис

let a : type::res = try{
    let a : int = 10;
    let b = func(a);
}
match(a.ex)
{
    ex::ok => { print("Ok"); }
    @default => { print(
                    f"Except: {res.ex.what} Line: {res.ex.line}"
                    ); }
    @finally => { /* do smth */ }
}

Мы вызываем блок кода, далее мы сохраняем первое встречное исключение. Далее мы проверяем - что мы получили, или ex::ok вывод по умолчанию, если мы не встретили никаких ошибок. Если мы отловили какую-то ошибку - мы напечатаем имя исключения и строку где она произошла. Самым последним исполнится блок ex::finally Заход в этот блок выполняется всегда после работы кода до него, даже если мы вернулись return.

Ужатый синтаксис

match((try func()).ex)
{
    ex::ok => print(f"ok {@.res}"); // @ обозначает анонимный
                                    // объект в скобках конструкции
    @default => print("not ok");
}