#pragma once #include /** * @brief The reproduction of Rust Option type. * @details * After writing programs in Rust, I deeply recognized the advantages of Rust and its indispensable infrastructure Result. * Therefore, introducing Result into C++ to enhance coding safety is essential. * I've done my best to simulate Rust's \c Result and its members \c Ok and \c Err (actually, I had DeepSeek simulate them). * * Why not write it in C++ style? Because C++'s way of using \c Result is too ugly and not explicit enough. * In C++'s approach, the expected value is returned directly, * and when encountering void specialization, you must write a pair of curly braces, which is very unclear. * For unexpected values, you need to manually construct \c std::unexpected, which is even more painful. * If you need in-place construction of unexpected values, you even need to put \c std::in_place as the first parameter of the constructor, * otherwise \c std::unexpected 's constructor won't forward the subsequent parameters to the unexpected value's constructor. * * In the \c Result type, type \c E can be any value according to your needs. * In Rust, an unexpected value type \c Ea can be converted to another unexpected value type \c Eb. * This feature is implemented through the \c From trait, allowing you to safely wrap one type of unexpected value into another in a function. * But in C++, we have C++ ways to do the same thing. * Assuming for each type \c E, we define a separate struct to describe them, * then we just need to add some extra constructors to the struct to convert them from one type to another. * * For example, type \c Ea is a struct named \c IoError. * In this struct, there is a member of type \c IoErrorKind indicating the category of this IO error. * At the same time, it has a constructor with its own type as the only parameter, used to construct (copy or move) itself. * Now in a function, we want to convert it to another type \c Eb named \c SystemError . * All you need to do is create a new struct named \c SystemError, then write all necessary constructors and other functions for it. * Then, the key point is to add a constructor with parameter type const IoError&. * This way, we can simply convert type \c Ea to type \c Eb through calls like: Err>(result.error());. * * In Rust, if you want to get human-readable descriptions of unexpected values, you must implement the \c Display trait. * But you don't need to do this in C++, you must write your own conversion functions to adapt to various output requirements. * For example, when using \c std::format, you need to write suitable formatting adapters for \c std::format. * Similarly, when using \c std::cerr 's \c operator<< overload, you also need to write suitable adapters. * @remarks This namespace only work with environment supporting `std::expected` (i.e. C++ 23). */ namespace yycc::result { /** * @brief Equivalent Rust \c Result in C++ * @tparam T The type of the expected value. * @tparam E The type of the unexpected value. */ template using Result = std::expected; /** * @brief Equvialent Rust \c Result::Ok in C++. * @tparam ResultType The type of the Result instance. * @param[in] args The arguments for building expected value. * @return An built Result instance with expected value. */ template ResultType Ok(Args &&...args) { using T = ResultType::value_type; if constexpr (!std::is_void_v) { return ResultType(std::in_place, std::forward(args)...); } else { static_assert(sizeof...(Args) == 0, "Ok cannot accept arguments"); return ResultType(std::in_place); } } /** * @brief Equvialent Rust \c Result::Err in C++. * @tparam ResultType The type of the Result instance. * @param[in] args The arguments for building unexpected value. * @return An built Result instance with unexpected value. */ template ResultType Err(Args &&...args) { return ResultType(std::unexpect, std::forward(args)...); } } // namespace yycc::result