Здравствуйте, alex_public, Вы писали:
_>Всё вышеописанное касается приложений написанных на любых языках. Но в большинстве языков есть один встроенный механизм обработки, который разработчики из-за незнания/неумения/безысходности используют для всех ситуаций. От этого и возникают все подобные неоднозначности и холивары.
_>Rust же здесь выгодного отличается наличием двух отдельных механизмов, каждый под свою ситуацию. Паники для обработки исключительных ситуаций и Result для обычных ошибок.
Чем это отличается? Что мешает в C++? У меня для плюсов, кстати, написан свой Result. Иногда использую. Иногда нет. Иногда использую исключения, когда ошибку не исправить, но можно отловить на каком-то уровне и вывести пользователю или записать в лог и похромать дальше — что-то типа assert'а, только более контролируемого
Здравствуйте, alex_public, Вы писали:
_>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result.
Ошибки могут возникать на всех уровнях, а ловить удобно на одном. Пробрасывать Result руками через 100500 уровней — то ещё удовольствие. Поэтому нет, не то же самое
_>А насчёт твоего универсального обработчика ошибок... Так он всегда возвращает именно 500? А если где-то надо 404 вернуть или 401, то как тогда? А если случается OOM, то он тоже пытается вернуть запрос (на создание которого опять же нужна память, которой уже нет) с 500?
404 или 401 — их возвращают согласно бизнес логике. А 5XX — это как раз "произошла какая-то неведомая херня, как обработать не знаем, мы сломались"
vsb>>Поэтому мне что раст, что голанг жутко неудобны и я не понимаю, как на них люди могут что-то писать. А вот обычные тупые исключения вроде питоновских — то, что доктор прописал.
_>А что именно неудобно в работе с Result?
Его нужно постоянно проверять Вообще, жизнь без постоянной проверки ошибок везде и всегда становится гораздо приятнее. Я вот сейчас пилю проект, там много разных конфигов. Я просто считаю, что там всё хорошо. Если где-то внизу что-то не так, просто кидаю исключение. Обработка ошибок — на верхнем уровне в одном месте, и всё. Сообщаю об ошибке и умываю руки. Потом можно будет переделать — сообщаю об ошибке и гружу дефолтные прибинденные конфиги, и хромаю с ними. Или можно будет добавить отправку репорта на сервер. Или ещё что-то. И всё в одном месте, и без лишних телодвижений на всех уровнях по пробросу ошибок наверх
Я делал как-то свой Result. Что-то типа пары {код_ошибки, результат}, результат валиден, если код ошибки — НЕТ_ОШИБКИ. Иногда использую, но в целом — не очень.
Еще делал мега подсистему для работы с ошибками. Хранил в TLS для каждого потока. Что-то типа GetLastError на стероидах. Можно было делать цепочки ошибок — если где-то внизу произошла ошибка, то можно было не просто её отправить выше, а подвесить ещё свою интерпретацию. До пользователя доходит последнее, но при желании он может посмотреть всю цепочку событий. В принципе, получилось неплохо, до сих пор использую иногда. Там просто другая проблема — получилось уже что-то фреймворка, не всегда его тащит хочется
Здравствуйте, Marty, Вы писали:
TB>>Немного более утомительно, чем TB>>
TB>>auto Result = foo()?;
TB>>
M>А что тут вообще происходит?
Тут происходит ровно то, что я написал в комментарии выше. Проверка наличия ошибки, немедленный возврат ошибки при её наличии, или вернуть результат если ошибки нет.
Кстати, а где в твоём примере разные коды ошибок? Где немедленный возврат из функции в случае ошибки? Больше похоже на Optional.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Кстати, а где в твоём примере разные коды ошибок?
В Result есть поле error
TB>Где немедленный возврат из функции в случае ошибки?
Такого нету, надо через if. Но можно намакросить, будет достаточно коротко
TB>Больше похоже на Optional.
Это он почти и есть, с некоторым дополнениями. Есть ещё Error, который почти как Result, но содержит только код ошибки. Наверно оно аналогично Result<void>, я уже не помню, нафига я его отдельно сделал. Error и Result умеют конвертироваться друг в друга. Есть ещё хитрый оператор !, точно не помню как работает, смотреть лень, он нужен для немного странной записи:
[сcode]
if (auto res = getResult())
{
// Делаем полезное с валидным результатом
}
if (auto res = !getResult())
{
// Результат не валиден
return Error(res);
}
[/сcode]
А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
Здравствуйте, Marty, Вы писали:
M>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[10]: Почему в расте отсутствует выброс исключений?
Трюк с оператором ! вот какой — он просто ставит в поле ошибки флаг, оператор преобразования в bool его сбрасывает, т.е. первая проверка на истинность в if'е восстанавливает нормальный код ошибки
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Error operator!() const
{
return Error(result | errors::invertFlag);
}
Ну, и ещё я не доделал случай, когда конструктор по умолчанию недоступен
errors.h
#pragma once
//----------------------------------------------------------------------------
/*! \file
\brief Ошибки, и связанные с ними определения
*/
//----------------------------------------------------------------------------#include"umba/preprocessor.h"#include"umba/stl.h"#if defined(UMBA_WIN32_USED)
#include <stdio.h>
#endif//----------------------------------------------------------------------------
//----------------------------------------------------------------------------namespace umba{
typedef uint32_t ErrorCode; //!< Сырой целочисленный тип кода ошибки
//----------------------------------------------------------------------------namespace errors
{
const ErrorCode errorFlag = 0x80000000; //!< Флаг - ошибкаconst ErrorCode invertFlag = 0x40000000; //!< Флаг инверсииconst ErrorCode errorSubsystemMask = 0x0F000000; //!< Маска подсистемыconst ErrorCode errorSubsystemUmba = 0x00000000; //!< Подсистема UMBAconst ErrorCode errorSubsystemWindows = 0x01000000; //!< Подсистема Windows - проброс виндового кода ошибкиconst ErrorCode errorSubsystemPosix = 0x02000000; //!< Подсистема Posix - проброс Posix ошибки - Linux ошибка или (под Windows) - ошибка CRTconst ErrorCode errorSubsystemQt = 0x03000000; //!< Подсистема Qt - ошибка фреймворка Qt
//! Код ошибки из виндового кодаinline ErrorCode makeFromWindows( ErrorCode errorCode )
{
return errorFlag | errorSubsystemWindows | (errorCode & ~(errorSubsystemMask | errorFlag));
}
//! Код ошибки из Posix кодаinline ErrorCode makeFromPosix( ErrorCode errorCode )
{
return errorFlag | errorSubsystemPosix | (errorCode & ~(errorSubsystemMask | errorFlag));
}
//! Код ошибки из Qt кодаinline ErrorCode makeFromQt( ErrorCode errorCode )
{
return errorFlag | errorSubsystemQt | (errorCode & ~(errorSubsystemMask | errorFlag));
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifdef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE
#endif
#ifdef UMBA_DECLARE_ERROR_CODE_ALIAS
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
#endif/* info */#define UMBA_DECLARE_ERROR_CODE(name, code, info) \
const ErrorCode name = code;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info) \
const ErrorCode name = code;
#endif/* DOXYGEN_SHOULD_SKIP_THIS */#include"zz_error_codes.h"#ifndef DOXYGEN_SHOULD_SKIP_THIS
#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
} // namespace errors
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------template <typename TValue>
struct Result;
#if 0
//! Конструктор по умолчанию
//! Конструктор из объекта "ошибка"
//! Конструктор из кода ошибки и результата (не все ошибки - фатальные)
//! Конструктор копирования
//! Конструктор перемещения#endif//! Плейс-холдер для кода ошибкиclass Error
{
public:
mutable ErrorCode result; //!< Код ошибки
//! Конструктор по умолчанию - создаёт "Ок - Нет ошибки"
Error() : result(errors::ok) {}
//! Конструктор из кода ошибки
Error(ErrorCode ec) : result(ec) {}
//! Конструктор копирования
Error(const Error &e) : result(e.result) {}
//! Оператор присваивания кода ошибки
Error& operator=(const ErrorCode &e) { result = e; return *this; }
//! Оператор присваивания
Error& operator=(const Error &e) { result = e.result; return *this; }
//! Преобразование в Resulttemplate <typename TValue>
Error( const Result<TValue> &r );
//! Проверка на сбойbool isFail() const
{
return (result & errors::errorFlag) ? true : false;
}
//! Проверка на успехbool isSuccess() const
{
return !isFail();
}
//! Проверка на полный успехbool isClean() const
{
return result == 0;
}
//! Использование в операторе if и сравнение с bool
/*! Проверка на успешность вызова:
if (auto a = someFunction()) { Ветка по успеху }
*/
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Error operator!() const
{
return Error(result | errors::invertFlag);
}
#ifdef UMBA_MCU_USED
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// errors:: #define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return UMBA_STRINGIFY(name);
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает строку с сообщением об ошибкеconst char* what() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
return"Unknown error";
}
};
}
//! Возвращает Unicode строку сообщением об ошибкеconst wchar_t* whatWide() const
{
return L"Some error";
}
#else
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// errors:: #define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return info;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает строку с сообщением об ошибкеconst char* what() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
#if defined(_WIN32) || defined(WIN32)
ErrorCode cleanCode = result & ~(errorSubsystemMask|errorFlag|invertFlag);
const char* fmt = "Unknown error %d (0x%08X)";
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: fmt = "Umba error %d (0x%08X)"; break;
case errorSubsystemWindows: fmt = "Windows error %d (0x%08X)"; break;
case errorSubsystemPosix: fmt = "Posix error %d (0x%08X)"; break;
case errorSubsystemQt: fmt = "Qt error %d (0x%08X)"; break;
//default: return "Unknown error";
};
#if defined(UMBA_CXX_HAS_STD11)
thread_local
#else
#if defined(_MSC_VER)
__declspec( thread )
#endif
#endif
static char buf[64];
sprintf(buf, fmt, cleanCode, result);
return buf;
#else
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: return"Umba error";
case errorSubsystemWindows: return"Windows error";
case errorSubsystemPosix: return"Posix error";
case errorSubsystemQt: return"Qt error";
default: return"Unknown error";
};
#endif
}
};
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
//errors::#define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return L##info;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает Unicode строку сообщением об ошибкеconst wchar_t* whatWide() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
#if defined(_WIN32) || defined(WIN32)
ErrorCode cleanCode = result & ~(errorSubsystemMask|errorFlag|invertFlag);
const wchar_t* fmt = L"Unknown error %d (0x%08X)";
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: fmt = L"Umba error %d (0x%08X)"; break;
case errorSubsystemWindows: fmt = L"Windows error %d (0x%08X)"; break;
case errorSubsystemPosix: fmt = L"Posix error %d (0x%08X)"; break;
case errorSubsystemQt: fmt = L"Qt error %d (0x%08X)"; break;
//default: return "Unknown error";
};
#if defined(UMBA_CXX_HAS_STD11)
thread_local
#else
#if defined(_MSC_VER)
__declspec( thread )
#endif
#endif
static wchar_t buf[64];
swprintf(buf, fmt, cleanCode, result);
return buf;
#else
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: return L"Umba error";
case errorSubsystemWindows: return L"Windows error";
case errorSubsystemPosix: return L"Posix error";
case errorSubsystemQt: return L"Qt error";
default: return L"Unknown error";
};
#endif
}
};
}
#endif
}; // struct Error
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//! Проверка двух Error между собой на равенствоinline
bool operator==(Error e1, Error e2)
{
return e1.result == e2.result;
}
//------------------------------
//! Проверка двух Error между собой на неравенствоinline
bool operator!=(Error e1, Error e2)
{
return e1.result != e2.result;
}
//------------------------------
//! Проверка Error и ErrorCode на равенствоinline
bool operator==(Error e1, ErrorCode e2)
{
return e1.result == e2;
}
//------------------------------
//! Проверка Error и ErrorCode на неравенствоinline
bool operator!=(Error e1, ErrorCode e2)
{
return e1.result != e2;
}
//------------------------------
//! Проверка ErrorCode и Error на равенствоinline
bool operator==(ErrorCode e1, Error e2)
{
return e1 == e2.result;
}
//------------------------------
//! Проверка ErrorCode и Error на неравенствоinline
bool operator!=(ErrorCode e1, Error e2)
{
return e1 != e2.result;
}
//------------------------------
//! Проверка Error и bool на равенство - Error равен true, когда он успешенinline
bool operator==(Error e1, bool e2)
{
return e1.isSuccess() == e2;
}
//------------------------------
//! Проверка Error и bool на неравенство - Error равен true, когда он успешенinline
bool operator!=(Error e1, bool e2)
{
return e1.isSuccess() != e2;
}
//------------------------------
//! Проверка bool и Error на равенство - Error равен true, когда он успешенinline
bool operator==(bool e1, Error e2)
{
return e1 == e2.isSuccess();
}
//------------------------------
//! Проверка bool и Error на неравенство - Error равен true, когда он успешенinline
bool operator!=(bool e1, Error e2)
{
return e1 != e2.isSuccess();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//! Обобщенный результат
/*! Данный класс позволяет возвращать код результата (ошибки)
одновременно с полезной нагрузкой. Когда полезная нагрузка не нужна,
следует использовать класс Error
*/template <typename TValue>
struct Result : public Error
{
public:
TValue value; //!< Значение результата
//! Конструктор по умолчанию
Result( )
: Error()
, value()
{}
//! Конструктор из объекта "ошибка"
Result( const Error &e )
: Error(e.result)
, value()
{}
//! Конструктор из кода ошибки и результата (не все ошибки - фатальные)
Result( ErrorCode ec, const TValue &tv )
: Error(ec)
, value(tv)
{}
//! Конструктор копирования
Result( const Result &r )
: Error(r.result)
, value(r.value)
{}
#if defined(UMBA_CXX_HAS_STD11)
//! Конструктор перемещения
Result(Result &&r)
: Error(r.result)
, value()
{
value = std::move(r.value);
}
#endif//! Конструктор из значения результата
Result( const TValue &tv )
: Error(0)
, value(tv)
{}
/*
explicit operator Error() const
{
return Error(result);
}
*/
//! Оператор присваивания
Result& operator=(const Result &r)
{
if (&r==this) return *this;
result = r.result;
value = r.value;
return *this;
}
#if defined(UMBA_CXX_HAS_STD11)
//! Присваивающий оператор присваивания
Result& operator=(Result &&r)
{
if (&r==this) return *this;
result = std::move(r.result);
value = std::move(r.value);
return *this;
}
#endif//! Использование в операторе if и сравнение с bool
/*! Проверка на успешность вызова:
if (auto a = someFunction()) { Ветка по успеху }
if (auto a = !someFunction()) { Ветка по неудаче }
*/
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Result operator!() const
{
return Result( result | errors::invertFlag, value );
}
}; // struct Resulttemplate <typename TValue>
inline
Error::Error( const Result<TValue> &r ) : result(r.result)
{
}
} // namespace umba
zz_error_codes.h
/*! \file
\brief Коды ошибок
*/
UMBA_DECLARE_ERROR_CODE (ok , 0, "Ok, no error")
UMBA_DECLARE_ERROR_CODE_ALIAS(success , ok, "Ok, operation successfully completed")
UMBA_DECLARE_ERROR_CODE_ALIAS(done , ok, "Ok, operation done")
UMBA_DECLARE_ERROR_CODE_ALIAS(no_error , ok, "Ok, no error")
UMBA_DECLARE_ERROR_CODE_ALIAS(bool_true , ok, "True condition")
UMBA_DECLARE_ERROR_CODE (already , 1, "Ok, but already done")
UMBA_DECLARE_ERROR_CODE (need_reopen , 2, "Device need to be reopened before changes will take effect")
UMBA_DECLARE_ERROR_CODE(bool_false , 0 | errorFlag, "False condition")
UMBA_DECLARE_ERROR_CODE(fail , 1 | errorFlag, "Generic fail")
UMBA_DECLARE_ERROR_CODE(not_implemented , 2 | errorFlag, "Method/function not implemented")
UMBA_DECLARE_ERROR_CODE(would_block , 3 | errorFlag, "Blocking mode required / Operation would block")
UMBA_DECLARE_ERROR_CODE(connection_in_progess , 4 | errorFlag, "Asynchonous connection in progress")
UMBA_DECLARE_ERROR_CODE(shutdown_in_progess , 5 | errorFlag, "Asynchonous shutdown in progress")
UMBA_DECLARE_ERROR_CODE(wrong_io_mode , 6 | errorFlag, "Wrong IO mode - attempt to read on write only, attemp to write to read only etc")
UMBA_DECLARE_ERROR_CODE(bus_addr_not_bound , 7 | errorFlag, "Attempt to write to bus stream, but device bus address not bound")
UMBA_DECLARE_ERROR_CODE(invalid_bus_addr , 8 | errorFlag, "Invalid bus adress taken")
UMBA_DECLARE_ERROR_CODE(unknown_bus_addr , 9 | errorFlag, "Unknown bus adress")
UMBA_DECLARE_ERROR_CODE(not_required , 10 | errorFlag, "Operation not required")
UMBA_DECLARE_ERROR_CODE(permission_denied , 11 | errorFlag, "Permission denied")
UMBA_DECLARE_ERROR_CODE(not_found , 12 | errorFlag, "Requested object not found")
UMBA_DECLARE_ERROR_CODE(device_not_opened , 13 | errorFlag, "Device is not opened")
UMBA_DECLARE_ERROR_CODE(device_busy , 14 | errorFlag, "Device is busy (temporary)")
UMBA_DECLARE_ERROR_CODE(device_already_online , 15 | errorFlag, "Device is already opened/online")
UMBA_DECLARE_ERROR_CODE(device_required_online , 16 | errorFlag, "Device must be online to perform this operation")
UMBA_DECLARE_ERROR_CODE(device_removed , 17 | errorFlag, "Device unexpectedly removed from the system")
UMBA_DECLARE_ERROR_CODE(read_failed , 18 | errorFlag, "Read operation failed (generic read error)")
UMBA_DECLARE_ERROR_CODE(write_failed , 19 | errorFlag, "Write operation failed (generic write error)")
UMBA_DECLARE_ERROR_CODE(not_supported , 20 | errorFlag, "Operation is not supported")
UMBA_DECLARE_ERROR_CODE(prohibited , 21 | errorFlag, "Operation prohibited by the underlying layer")
UMBA_DECLARE_ERROR_CODE(timed_out , 22 | errorFlag, "Operation was timed out")
UMBA_DECLARE_ERROR_CODE(invalid_param , 23 | errorFlag, "Invalid parameter taken")
UMBA_DECLARE_ERROR_CODE(invalid_state , 24 | errorFlag, "Object is in invalid state")
UMBA_DECLARE_ERROR_CODE(invalid_prerequisites , 25 | errorFlag, "Invalid prerequisites")
UMBA_DECLARE_ERROR_CODE(protocol_not_ready , 26 | errorFlag, "Protocol not ready to perform this operation")
UMBA_DECLARE_ERROR_CODE(too_much_data , 27 | errorFlag, "Too much data")
UMBA_DECLARE_ERROR_CODE(stream_not_assigned , 28 | errorFlag, "Stream not assigned")
UMBA_DECLARE_ERROR_CODE(unknown_interface , 29 | errorFlag, "Unknown (unsupported) interface")
UMBA_DECLARE_ERROR_CODE(header_crc_error , 30 | errorFlag, "Header CRC error")
UMBA_DECLARE_ERROR_CODE(crc_error , 31 | errorFlag, "CRC error")
UMBA_DECLARE_ERROR_CODE(out_of_sync , 32 | errorFlag, "Protocol is out of sync")
UMBA_DECLARE_ERROR_CODE(invalid_protocol_headeer , 33 | errorFlag, "Protocol header contains invalid data")
UMBA_DECLARE_ERROR_CODE(wellknown_addr_not_supported , 34 | errorFlag, "Wellknown address not supperted")
UMBA_DECLARE_ERROR_CODE(temporary_failure , 35 | errorFlag, "Temporary failure (often hardware)")
UMBA_DECLARE_ERROR_CODE(too_many_opened , 36 | errorFlag, "Too many opened files (sockets, handles etc)")
UMBA_DECLARE_ERROR_CODE(error_aborted , 37 | errorFlag, "The operation was aborted")
UMBA_DECLARE_ERROR_CODE(open_error , 38 | errorFlag, "Open operation generic error")
UMBA_DECLARE_ERROR_CODE(resize_error , 39 | errorFlag, "Can't resize stream/IO device (is it pipe etc?)")
UMBA_DECLARE_ERROR_CODE(not_seekable , 40 | errorFlag, "IO device not seekable")
UMBA_DECLARE_ERROR_CODE(file_operation_error , 41 | errorFlag, "File operation error (remove/rename/copy/etc)")
UMBA_DECLARE_ERROR_CODE(eof_reached , 42 | errorFlag, "End of file reached")
//UMBA_DECLARE_ERROR_CODE( , | errorFlag, "")
Здравствуйте, T4r4sB, Вы писали:
M>>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
TB>Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Здравствуйте, Marty, Вы писали:
M>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
M>>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
А, ясно.
А я делал немного не так — у меня success состояние может содержать какое-то значение, типа всё хорошо, но были кой какие проблемки. В COM-овском HRESULT стырил идею
Здравствуйте, vaa, Вы писали:
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления. vaa>Или быть может существует более продвинутый механизм наподобие Common Lisp Condition System ?
На мой взгляд совершенно неудобный. По крайней мере в C++ это все просто ужасно,
как будто пишешь обработчик прерываний, который должен работать в условиях возможных вложенных прерываний,
а что произойдет если `operator=` выбросить исключение, а что произойдет если при передаче параметра в функцию,
но до ее вызова будет исключение и т.д. и т.п.
Предсказуемость и понимание того, что будет если эта функция завершиться с ошибкой,
а также контроль во время компиляции того, что `Result` будет обработан,
дорого стоит.
Ну плюс отсутствие исключение позволяет оптимизатору компилятора действительно в некоторых
случаях абстракции которые как бы "zero cost" превращать в действительно "zero cost".
Был пару лет назад доклад про `std::unique_ptr` и там были фундаментальные проблемы к сведению его
к простому указателю на в генерируемом ассебмлерном коде в том числе и из-за исключений.
Re[11]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
Либо более высокоуровневая ошибка должна реализовывать трейт From для исходной ошибки. Тогда просто оператора "?" достаточно безо всяких map_err (хотя и map_err тоже полезная штука). В крейтах thiserror и anyhow есть полезные макросы и типы для того, чтобы не заморачиваться этим вручную.
Здравствуйте, ArtDenis, Вы писали:
AD>Либо более высокоуровневая ошибка должна реализовывать трейт From для исходной ошибки. Тогда просто оператора "?" достаточно безо всяких map_err (хотя и map_err тоже полезная штука). В крейтах thiserror и anyhow есть полезные макросы и типы для того, чтобы не заморачиваться этим вручную.
Да, хотел как раз об этом написать, обычно пишутся автоконверторы ошибок с помощью thiserror макросов, либо всё коллапсируется в единую строковую ошибку с колстеком (anyhow).
Первое нужно если хочется в интерфейсе какой то набор возможных ошибок, например io::error, db::error, и туда автоматически склеивать разнообразные ошибки с нижних уровней. Второе, если не хочется парится а просто вернуться при любой ошибке. Оператор '?' работает и там и там.
Re[5]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, FR, Вы писали:
FR>>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс TB>Но будь осторожен. Я комбо словил, случается именно в вин7 в х32 проге. Там паника это особая инструкция которая кидает исключение, а винда гасит исключения, что произошли внутри вндПрок, просто гасит и продолжает исполнение
И..? А что ожидалось? Паникам и исключениям нельзя пересекать языковые барьеры, так как ОС не знает о Rust, а Rust — об исключениях ОС и С++.
Re[6]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Marty, Вы писали:
M>>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
TB>Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Не только. try работает с конвертацией типрв, поэтому если BarError умеет создаваться из FooError (impl From<FooError> for BarError), то ? работает прозрачно: foo()?;
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Marty, Вы писали:
M>>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, flаt, Вы писали:
F>>И..? А что ожидалось?
TB>А почему нельзя просто завалить приложение?! Почему для х64 так и сделано, а для х86, запускаемом на х64, нет?