{"id":486735,"date":"2026-07-09T08:24:15","date_gmt":"2026-07-09T08:24:15","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=486735"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=486735","title":{"rendered":"\u041f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0435\u0447\u0435\u0441\u0442\u043d\u043e\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 std::expected \u0432\u00a0C++23 \u0438 core::result::Result \u0432 Rust"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440!<\/p>\n<p>\u0412\u043e\u043e\u0434\u0443\u0448\u0435\u0432\u0438\u0432\u0448\u0438\u0441\u044c \u0441\u0442\u0430\u0442\u044c\u0451\u0439 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/1054592\/\" rel=\"noopener noreferrer nofollow\">std::expected \u0432\u00a0C++23: \u0433\u0430\u0439\u0434 \u043f\u043e\u00a0\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u00a0\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043d\u0430\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 error handling<\/a>, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0441 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Rust. \u041a\u043e\u0434 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438\u0442\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u0442\u0441\u044f.<\/p>\n<hr\/>\n<p>\u041e \u0441\u0435\u0431\u0435: \u043a\u043e\u0434 \u043d\u0430 \u0421++ \u0432\u0438\u0436\u0443 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0440\u0430\u0431\u043e\u0442\u0430\u0445 \u043f\u0435\u0440\u0432\u043e\u043a\u0443\u0440\u0441\u043d\u0438\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0434\u0430\u043b\u044c\u0448\u0435 \u0421++11 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u043e\u0431\u0431\u0438 \u0438\u043d\u043e\u0433\u0434\u0430 \u043f\u0438\u0448\u0443 \u043d\u0430 Rust.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/1054592\/\" rel=\"noopener noreferrer nofollow\">\u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438<\/a> \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0443 <code>try\/catch<\/code> \u043d\u0430 <code>std::expected<\/code>, \u043f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u044e:<\/p>\n<pre><code class=\"cpp\">#include &lt;expected&gt;#include &lt;string&gt;#include &lt;iostream&gt;enum class ParseError {    InvalidFormat,    OutOfRange,    EmptyInput};std::expected&lt;int, ParseError&gt; parse_positive_int(const std::string&amp; input) {    if (input.empty()) {        return std::unexpected(ParseError::EmptyInput);    }    try {        int value = std::stoi(input);        if (value &lt; 0) {            return std::unexpected(ParseError::OutOfRange);        }        return value;    } catch (...) {        return std::unexpected(ParseError::InvalidFormat);    }}int main() {    auto result = parse_positive_int(\"42\");    if (result) {        std::cout &lt;&lt; \"Parsed: \" &lt;&lt; *result &lt;&lt; '\\n';    } else {        std::cout &lt;&lt; \"Error code: \" &lt;&lt; static_cast&lt;int&gt;(result.error()) &lt;&lt; '\\n';    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410 \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430 Rust \u043d\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435 \u043f\u0438\u0448\u0443\u0442, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 (\u0448\u0443\u0442\u043a\u0430), \u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c:<\/p>\n<pre><code class=\"rust\">#[derive(Debug)]enum ParseError {    InvalidFormat,    OutOfRange,    EmptyInput,}fn parse_positive_int(input: &amp;String) -&gt; Result&lt;i32, ParseError&gt; {    if input.is_empty() {        return Err(ParseError::EmptyInput);    }    match input.parse() {        Ok(value) if value &lt; 0 =&gt; Err(ParseError::OutOfRange),        Ok(value) =&gt; Ok(value),        Err(_) =&gt; Err(ParseError::InvalidFormat),    }}fn main() {    let result = parse_positive_int(&amp;\"42\".to_string());    if result.is_ok() {        println!(\"Parsed: {}\", result.unwrap());    } else {        println!(\"Error code: {}\", result.unwrap_err() as u32);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u0447\u0451\u043c \u043d\u0435\u0447\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u044c: \u0432 Rust \u043d\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439, \u0438 \u043f\u043e \u043c\u043e\u0435\u043c\u0443 \u043c\u043d\u0435\u043d\u0438\u044e \u043e\u043d \u044d\u0442\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0421\u0438. \u0422\u0430\u043a \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>parse_positive_int<\/code> \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438. \u0424\u0443\u043d\u043a\u0446\u0438\u044f main \u0442\u043e\u0436\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043e\u0448\u0438\u0431\u043e\u043a, \u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u043d\u0435\u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e, \u0432\u044b\u0437\u043e\u0432\u044b \u043c\u0435\u0442\u043e\u0434\u043e\u0432 <code>.unwrap()<\/code> \u0438 <code>.unwrap_err()<\/code> \u0431\u0443\u0434\u0443\u0442 \u043f\u0430\u043d\u0438\u043a\u043e\u0432\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432 <code>Result<\/code> \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442.<\/p>\n<p>\u0415\u0449\u0451 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0421++ \u043a\u043e\u0434\u0430: \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0443 <code>Debug<\/code> \u0443 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 <code>.unwrap()<\/code> \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0430 \u0435\u0451, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0441\u043f\u0435\u0447\u0430\u0442\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u043f\u0430\u043d\u0438\u043a\u0435.<\/p>\n<p>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0432\u043d\u0435\u0441\u0451\u043c \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f, \u0438 \u0434\u0432\u0438\u043d\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435:<\/p>\n<pre><code class=\"rust\">fn parse_positive_int(input: &amp;str) -&gt; Result&lt;i32, ParseError&gt; {    if input.is_empty() {        return Err(ParseError::EmptyInput);    }    match input.parse() {        Ok(value) if value &lt; 0 =&gt; Err(ParseError::OutOfRange),        Ok(value) =&gt; Ok(value),        Err(_) =&gt; Err(ParseError::InvalidFormat),    }}fn main() {    let result = parse_positive_int(\"42\");    match result {        Ok(value) =&gt; println!(\"Parsed: {}\", value),        Err(err) =&gt; println!(\"Error code: {}\", err as u32),    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0437\u043c\u0435\u043d\u0451\u043d \u0442\u0438\u043f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0439 \u0441\u0440\u0435\u0437, \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0442\u0440\u043e\u043a\u0438, \u0447\u0442\u043e \u043d\u0435 \u0435\u0441\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u043e. \u0418 \u0431\u043e\u043b\u0435\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0431\u0435\u0437 \u043f\u0430\u043d\u0438\u043a. \u0415\u0449\u0451 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 <code>as<\/code> \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0430\u0434\u0451\u0436\u043d\u043e, \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<pre><code class=\"cpp\">auto result = parse_positive_int(\"abc\");int value = result.value_or(0);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043d\u0430 Rust:<\/p>\n<pre><code class=\"rust\">let result = parse_positive_int(\"42\");let value = result.unwrap_or(0);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0430\u0437\u0432\u0435 \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u0431\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0442\u0438\u043f\u0430 &#171;auto&#187;.<\/p>\n<blockquote>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440: \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0431\u0430\u0437\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 DTO.<\/p>\n<\/blockquote>\n<pre><code class=\"cpp\">struct User {    int id;    std::string name;    int role_id;};enum class AppError {    UserNotFound,    AccessDenied,    DatabaseError};std::expected&lt;User, AppError&gt; load_user(int id);std::expected&lt;bool, AppError&gt; check_admin(const User&amp; user);std::expected&lt;UserDto, AppError&gt; to_dto(const User&amp; user);\/\/ \u0426\u0435\u043f\u043e\u0447\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 and_thenstd::expected&lt;UserDto, AppError&gt; get_admin_dto(int id) {    return load_user(id)        .and_then([](const User&amp; user) -&gt; std::expected&lt;User, AppError&gt; {            return check_admin(user)                .and_then([&amp;user](bool is_admin) -&gt; std::expected&lt;User, AppError&gt; {                    if (!is_admin) return std::unexpected(AppError::AccessDenied);                    return user;                });        })        .and_then(to_dto);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0430\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043a\u043b\u0430\u0441\u0441 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u043e\u043f\u044f\u0442\u044c \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c, \u0430 \u043f\u043e\u0442\u043e\u043c \u0443\u043b\u0443\u0447\u0448\u0438\u043c:<\/p>\n<pre><code class=\"rust\">struct User {    id: i32,    name: String,    role_id: i32,}enum AppError {    UserNotFound,    AccessDenied,    DatabaseError,}\/\/ TODOstruct UserDto;fn load_user(id: i32) -&gt; Result&lt;User, AppError&gt; {    todo!()}fn check_admin(user: &amp;User) -&gt; Result&lt;bool, AppError&gt; {    todo!()}fn to_dto(user: &amp;User) -&gt; Result&lt;UserDto, AppError&gt; {    todo!()}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    load_user(id)        .and_then(|user| {            check_admin(&amp;user).and_then(|is_admin| {                if !is_admin {                    Err(AppError::AccessDenied)                } else {                    Ok(user)                }            })        })        .and_then(|user| to_dto(&amp;user))}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0432\u0435\u0440\u043d\u0443\u0432\u0448\u0438\u0439\u0441\u044f \u0438\u0437 <code>load_user()<\/code>. \u0412 \u0421++ \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435\u044f\u0432\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443, \u0432 Rust \u0432\u0441\u0451 \u0447\u0435\u0441\u0442\u043d\u043e \u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0443\u043a\u0430\u043c\u0438 \u0431\u0440\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0435\u0441\u0442\u0430\u0445. \u0410\u0445, \u0434\u0430, \u043d\u0438\u0433\u0434\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u0442\u0438\u043f\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 &#8212; \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0438\u0445 \u0441\u0430\u043c \u0438\u0437 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u0439.<\/p>\n<p>\u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0436\u0435\u043b\u0435\u0437\u043e\u0431\u0435\u0442\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u043d\u043e\u0435:<\/p>\n<pre><code class=\"rust\">fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = match load_user(id) {        Ok(user) =&gt; user,        Err(err) =&gt; return Err(err),    };    let is_admin = match check_admin(&amp;user) {        Ok(is_admin) =&gt; is_admin,        Err(err) =&gt; return Err(err),    };    if !is_admin {        return Err(AppError::AccessDenied);    }    to_dto(&amp;user)}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u0438\u043d\u0443\u0441 \u043e\u0434\u043d\u043e \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043d\u0438\u0435 (\u0438\u0437 <code>Ok(user)<\/code>)! \u041d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0443\u0442\u043e\u043c\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e, \u0435\u0441\u043b\u0438 \u043d\u0430\u0434\u043e \u043b\u0438\u0448\u044c \u043f\u0435\u0440\u0435\u043a\u0438\u043d\u0443\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u0418&#8230;<\/p>\n<pre><code class=\"rust\">fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    let is_admin = check_admin(&amp;user)?;    if !is_admin {        return Err(AppError::AccessDenied);    }    to_dto(&amp;user)}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0423\u0436\u0435 \u0447\u0438\u0449\u0435 \u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f, \u043d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u0438\u043c <code>to_dto()<\/code>:<\/p>\n<pre><code class=\"rust\">\/\/ TODOstruct UserDto;impl TryFrom&lt;&amp;User&gt; for UserDto {    type Error = AppError;    fn try_from(user: &amp;User) -&gt; Result&lt;Self, Self::Error&gt; {        todo!()    }}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    let is_admin = check_admin(&amp;user)?;    if !is_admin {        return Err(AppError::AccessDenied);    }    UserDto::try_from(&amp;user)}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u043a\u0430\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043c\u0430\u043f\u0438\u043d\u0433\u0430 \u0438\u0437 <code>User<\/code> \u0432 <code>UserDto<\/code>, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0438\u0448\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <code>TryFrom<\/code>.<\/p>\n<p>\u0418 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0438\u043c check_admin, \u0445\u043e\u0442\u044f \u044d\u0442\u043e \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u0435\u043c\u043e, \u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0447\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c:<\/p>\n<pre><code class=\"rust\">const ROLE_ID_ADMIN: i32 = 1;fn check_admin(user: &amp;User) -&gt; Result&lt;(), AppError&gt; {    let is_admin = user.role_id == ROLE_ID_ADMIN;    if !is_admin {        return Err(AppError::AccessDenied);    }    Ok(())}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    check_admin(&amp;user)?;    UserDto::try_from(&amp;user)}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0440\u0438 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>get_admin_dto()<\/code>. Nice!<\/p>\n<h2>\u0412\u043e\u0437\u0432\u0440\u0430\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a<\/h2>\n<p>Yay, \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0432 \u0441\u0442\u0430\u0442\u044c\u0435!<\/p>\n<p>\u041e\u043f\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438:<\/p>\n<pre><code class=\"cpp\">enum class DbError { ConnectionFailed, QueryFailed, RecordNotFound };enum class ServiceError { UserNotFound, InvalidState, DependencyFailed };enum class ApiError { NotFound, BadRequest, InternalError };std::expected&lt;User, DbError&gt; repo_find_user(int id);std::expected&lt;User, ServiceError&gt; service_get_user(int id) {    return repo_find_user(id)        .transform_error([](DbError db_err) -&gt; ServiceError {            switch (db_err) {                case DbError::RecordNotFound: return ServiceError::UserNotFound;                case DbError::ConnectionFailed: return ServiceError::DependencyFailed;                case DbError::QueryFailed: return ServiceError::DependencyFailed;            }            return ServiceError::DependencyFailed;        });}std::expected&lt;UserResponse, ApiError&gt; handler_get_user(int id) {    return service_get_user(id)        .transform([](const User&amp; user) { return UserResponse{user}; })        .transform_error([](ServiceError svc_err) -&gt; ApiError {            switch (svc_err) {                case ServiceError::UserNotFound: return ApiError::NotFound;                case ServiceError::InvalidState: return ApiError::BadRequest;                case ServiceError::DependencyFailed: return ApiError::InternalError;            }            return ApiError::InternalError;        });}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>&#171;\u0414\u0443\u0431\u043e\u0432\u0430\u044f&#187; \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c:<\/p>\n<pre><code class=\"rust\">struct User {    id: i32,    name: String,    role_id: i32,}struct UserResponse {    id: i32,    name: String,}impl UserResponse {    fn new(user: &amp;User) -&gt; Self {        Self {            id: user.id,            name: user.name.clone(),        }    }}enum DbError {    ConnectionFailed,    QueryFailed,    RecordNotFound,}enum ServiceError {    UserNotFound,    InvalidState,    DependencyFailed,}enum ApiError {    NotFound,    BadRequest,    InternalError,}fn repo_find_user(id: i32) -&gt; Result&lt;User, DbError&gt; {    todo!()}fn service_get_user(id: i32) -&gt; Result&lt;User, ServiceError&gt; {    repo_find_user(id).map_err(|db_err| match db_err {        DbError::ConnectionFailed =&gt; ServiceError::DependencyFailed,        DbError::QueryFailed =&gt; ServiceError::DependencyFailed,        DbError::RecordNotFound =&gt; ServiceError::UserNotFound,    })}fn handler_get_user(id: i32) -&gt; Result&lt;UserResponse, ApiError&gt; {    service_get_user(id)        .map(|user: User| UserResponse::new(&amp;user))        .map_err(|svc_err| match svc_err {            ServiceError::UserNotFound =&gt; ApiError::NotFound,            ServiceError::InvalidState =&gt; ApiError::BadRequest,            ServiceError::DependencyFailed =&gt; ApiError::InternalError,        })}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u043d\u043e\u0433\u043e\u0431\u0443\u043a\u0432. \u0412\u044b\u043d\u0435\u0441\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u0430\u043f\u0438\u043d\u0433\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0432\u043e <code>From<\/code>:<\/p>\n<pre><code class=\"rust\">impl From&lt;DbError&gt; for ServiceError {    fn from(value: DbError) -&gt; Self {        match value {            DbError::ConnectionFailed =&gt; ServiceError::DependencyFailed,            DbError::QueryFailed =&gt; ServiceError::DependencyFailed,            DbError::RecordNotFound =&gt; ServiceError::UserNotFound,        }    }}impl From&lt;ServiceError&gt; for ApiError {    fn from(value: ServiceError) -&gt; Self {        match value {            ServiceError::UserNotFound =&gt; ApiError::NotFound,            ServiceError::InvalidState =&gt; ApiError::BadRequest,            ServiceError::DependencyFailed =&gt; ApiError::InternalError,        }    }}fn service_get_user(id: i32) -&gt; Result&lt;User, ServiceError&gt; {    repo_find_user(id).map_err(ServiceError::from)}fn handler_get_user(id: i32) -&gt; Result&lt;UserResponse, ApiError&gt; {    service_get_user(id)        .map(|user: User| UserResponse::new(&amp;user))        .map_err(ApiError::from)}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u043e\u0449\u0435, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0437\u0430\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u044c From. \u042d\u0442\u043e \u0432\u0441\u0451 \u0435\u0449\u0451 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0442\u043e\u043c\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0441\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0435\u0440\u0443\u0442 \u0447\u0430\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u043d\u044b\u0435 \u043c\u0430\u043a\u0440\u043e\u0441\u044b (\u041f\u0420\u0415\u0414\u0423\u041f\u0420\u0415\u0416\u0414\u0415\u041d\u0418\u0415: \u043b\u043e\u0433\u0438\u043a\u0430 \u043c\u0430\u043f\u0438\u043d\u0433\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0434\u0440\u0443\u0433\u0430\u044f!).<\/p>\n<pre><code class=\"rust\">use thiserror::Error;#[derive(Error, Debug)]enum DbError {    #[error(\"Connection failed\")]    ConnectionFailed,    #[error(\"Query failed\")]    QueryFailed,}#[derive(Error, Debug)]enum ServiceError {    #[error(\"User not found\")]    UserNotFound,    #[error(\"Invalid state\")]    InvalidState,    #[error(\"Dependency failed: {0}\")]    DependencyFailed(#[from] DbError),}#[derive(Error, Debug)]enum ApiError {    #[error(\"Not found\")]    NotFound,    #[error(\"Bad request\")]    BadRequest,    #[error(\"Internal error: {0}\")]    InternalError(#[from] ServiceError),}fn service_get_user(id: i32) -&gt; Result&lt;User, ServiceError&gt; {    Ok(repo_find_user(id)?)}fn handler_get_user(id: i32) -&gt; Result&lt;UserResponse, ApiError&gt; {    Ok(UserResponse::new(&amp;service_get_user(id)?))}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043c\u0438\u043d\u0443\u0441\u044b: \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c <code>Display<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0436 \u0441\u043e\u0432\u0441\u0435\u043c \u043b\u0435\u043d\u044c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438, \u0442\u043e \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <code>anyhow<\/code>, \u043d\u043e \u0442\u0430\u043c \u0432\u043d\u0443\u0442\u0440\u0438 <code>Box&lt;dyn Error&gt;<\/code>&#8230;<\/p>\n<pre><code class=\"rust\">use anyhow::{Result, anyhow};#[derive(Debug)]enum DbError {    ConnectionFailed,    QueryFailed,}impl std::fmt::Display for DbError {    fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter&lt;'_&gt;) -&gt; std::fmt::Result {        match self {            DbError::ConnectionFailed =&gt; write!(f, \"Connection failed\"),            DbError::QueryFailed =&gt; write!(f, \"Query failed\"),        }    }}impl std::error::Error for DbError {}fn repo_find_user(id: i32) -&gt; Result&lt;User&gt; {    Err(anyhow!(DbError::ConnectionFailed))}fn service_get_user(id: i32) -&gt; Result&lt;User&gt; {    Ok(repo_find_user(id)?)}fn handler_get_user(id: i32) -&gt; Result&lt;UserResponse&gt; {    Ok(UserResponse::new(&amp;service_get_user(id)?))}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h2>\u0411\u043e\u0433\u0430\u0442\u044b\u0435 \u0442\u0438\u043f\u044b \u043e\u0448\u0438\u0431\u043e\u043a \u0441 \u0434\u0435\u0442\u0430\u043b\u044f\u043c\u0438<\/h2>\n<p>\u0426\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043d\u0430 \u0421++:<\/p>\n<pre><code class=\"cpp\">struct ValidationError {    std::string field;    std::string message;    std::optional&lt;std::string&gt; received_value;};struct NotFoundError {    std::string entity_type;    std::string identifier;};struct ApiError {    int http_status;    std::string code;    std::string message;    std::vector&lt;ValidationError&gt; validation_errors;};std::expected&lt;int, ApiError&gt; parse_age(const std::string&amp; input) {    if (input.empty()) {        return std::unexpected(ApiError{            .http_status = 400,            .code = \"EMPTY_FIELD\",            .message = \"Age cannot be empty\",            .validation_errors = {                {\"age\", \"Required field is missing\", std::nullopt}            }        });    }        try {        int value = std::stoi(input);        if (value &lt; 0 || value &gt; 150) {            return std::unexpected(ApiError{                .http_status = 400,                .code = \"OUT_OF_RANGE\",                .message = \"Age must be between 0 and 150\",                .validation_errors = {                    {\"age\", \"Value out of range\", input}                }            });        }        return value;    } catch (...) {        return std::unexpected(ApiError{            .http_status = 400,            .code = \"INVALID_FORMAT\",            .message = \"Age must be a valid integer\",            .validation_errors = {                {\"age\", \"Could not parse as integer\", input}            }        });    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 Rust, \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c:<\/p>\n<pre><code class=\"rust\">struct ValidationError {    field: String,    message: String,    received_value: Option&lt;String&gt;,}struct ApiError {    http_status: u16,    code: String,    message: String,    validation_errors: Vec&lt;ValidationError&gt;,}fn parse_age(input: &amp;str) -&gt; Result&lt;i32, ApiError&gt; {    if input.is_empty() {        return Err(ApiError {            http_status: 400,            code: \"EMPTY_FIELD\".to_string(),            message: \"Age cannot be empty\".to_string(),            validation_errors: vec![ValidationError {                field: \"age\".to_string(),                message: \"Required field is missing\".to_string(),                received_value: None,            }],        });    }    match input.parse() {        Ok(value) if value &lt; 0 || value &gt; 150 =&gt; Err(ApiError {            http_status: 400,            code: \"OUT_OF_RANGE\".to_string(),            message: \"Age must be between 0 and 150\".to_string(),            validation_errors: vec![ValidationError {                field: \"age\".to_string(),                message: \"Value out of range\".to_string(),                received_value: Some(input.to_string()),            }],        }),        Ok(value) =&gt; Ok(value),        Err(_) =&gt; Err(ApiError {            http_status: 400,            code: \"INVALID_FORMAT\".to_string(),            message: \"Age must be a valid integer\".to_string(),            validation_errors: vec![ValidationError {                field: \"age\".to_string(),                message: \"Could not parse as integer\".to_string(),                received_value: None,            }],        }),    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0430\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 (\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 serde) \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 json \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u043c\u044b\u0441\u043b\u0438\u043c\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"rust\">use serde::Serialize;#[derive(Serialize)]#[serde(rename_all = \"camelCase\")]struct ValidationError {    field: String,    message: String,    #[serde(skip_serializing_if = \"Option::is_none\")]    received_value: Option&lt;String&gt;,}#[derive(Serialize)]#[serde(rename_all = \"camelCase\")]struct ApiError {    http_status: u16,    code: String,    message: String,    #[serde(skip_serializing_if = \"Vec::is_empty\")]    validation_errors: Vec&lt;ValidationError&gt;,}fn main() {    let age = parse_age(\"152\");    if let Err(err) = age {        let json = serde_json::to_string_pretty(&amp;err).unwrap();        println!(\"{}\", json);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"json\">{  \"httpStatus\": 400,  \"code\": \"OUT_OF_RANGE\",  \"message\": \"Age must be between 0 and 150\",  \"validationErrors\": [    {      \"field\": \"age\",      \"message\": \"Value out of range\",      \"receivedValue\": \"152\"    }  ]}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1057332\/\">https:\/\/habr.com\/ru\/articles\/1057332\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440!\u0412\u043e\u043e\u0434\u0443\u0448\u0435\u0432\u0438\u0432\u0448\u0438\u0441\u044c \u0441\u0442\u0430\u0442\u044c\u0451\u0439 std::expected \u0432\u00a0C++23: \u0433\u0430\u0439\u0434 \u043f\u043e\u00a0\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u00a0\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043d\u0430\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 error handling, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0441 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Rust. \u041a\u043e\u0434 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438\u0442\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u0442\u0441\u044f.\u041e \u0441\u0435\u0431\u0435: \u043a\u043e\u0434 \u043d\u0430 \u0421++ \u0432\u0438\u0436\u0443 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0440\u0430\u0431\u043e\u0442\u0430\u0445 \u043f\u0435\u0440\u0432\u043e\u043a\u0443\u0440\u0441\u043d\u0438\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0434\u0430\u043b\u044c\u0448\u0435 \u0421++11 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u043e\u0431\u0431\u0438 \u0438\u043d\u043e\u0433\u0434\u0430 \u043f\u0438\u0448\u0443 \u043d\u0430 Rust.\u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0443 try\/catch \u043d\u0430 std::expected, \u043f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u044e:#include &lt;expected&gt;#include &lt;string&gt;#include &lt;iostream&gt;enum class ParseError {    InvalidFormat,    OutOfRange,    EmptyInput};std::expected&lt;int, ParseError&gt; parse_positive_int(const std::string&amp; input) {    if (input.empty()) {        return std::unexpected(ParseError::EmptyInput);    }    try {        int value = std::stoi(input);        if (value &lt; 0) {            return std::unexpected(ParseError::OutOfRange);        }        return value;    } catch (&#8230;) {        return std::unexpected(ParseError::InvalidFormat);    }}int main() {    auto result = parse_positive_int(&#171;42&#187;);    if (result) {        std::cout &lt;&lt; &#171;Parsed: &#187; &lt;&lt; *result &lt;&lt; &#8216;\\n&#8217;;    } else {        std::cout &lt;&lt; &#171;Error code: &#187; &lt;&lt; static_cast&lt;int&gt;(result.error()) &lt;&lt; &#8216;\\n&#8217;;    }}\u0410 \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430 Rust \u043d\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435 \u043f\u0438\u0448\u0443\u0442, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 (\u0448\u0443\u0442\u043a\u0430), \u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c:#[derive(Debug)]enum ParseError {    InvalidFormat,    OutOfRange,    EmptyInput,}fn parse_positive_int(input: &amp;String) -&gt; Result&lt;i32, ParseError&gt; {    if input.is_empty() {        return Err(ParseError::EmptyInput);    }    match input.parse() {        Ok(value) if value &lt; 0 =&gt; Err(ParseError::OutOfRange),        Ok(value) =&gt; Ok(value),        Err(_) =&gt; Err(ParseError::InvalidFormat),    }}fn main() {    let result = parse_positive_int(&amp;&#187;42&#8243;.to_string());    if result.is_ok() {        println!(&#171;Parsed: {}&#187;, result.unwrap());    } else {        println!(&#171;Error code: {}&#187;, result.unwrap_err() as u32);    }}\u0412 \u0447\u0451\u043c \u043d\u0435\u0447\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u044c: \u0432 Rust \u043d\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439, \u0438 \u043f\u043e \u043c\u043e\u0435\u043c\u0443 \u043c\u043d\u0435\u043d\u0438\u044e \u043e\u043d \u044d\u0442\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0421\u0438. \u0422\u0430\u043a \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f parse_positive_int \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438. \u0424\u0443\u043d\u043a\u0446\u0438\u044f main \u0442\u043e\u0436\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043e\u0448\u0438\u0431\u043e\u043a, \u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u043d\u0435\u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e, \u0432\u044b\u0437\u043e\u0432\u044b \u043c\u0435\u0442\u043e\u0434\u043e\u0432 .unwrap() \u0438 .unwrap_err() \u0431\u0443\u0434\u0443\u0442 \u043f\u0430\u043d\u0438\u043a\u043e\u0432\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432 Result \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442.\u0415\u0449\u0451 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0421++ \u043a\u043e\u0434\u0430: \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0443 Debug \u0443 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 .unwrap() \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0430 \u0435\u0451, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0441\u043f\u0435\u0447\u0430\u0442\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u043f\u0430\u043d\u0438\u043a\u0435.\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0432\u043d\u0435\u0441\u0451\u043c \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f, \u0438 \u0434\u0432\u0438\u043d\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435:fn parse_positive_int(input: &amp;str) -&gt; Result&lt;i32, ParseError&gt; {    if input.is_empty() {        return Err(ParseError::EmptyInput);    }    match input.parse() {        Ok(value) if value &lt; 0 =&gt; Err(ParseError::OutOfRange),        Ok(value) =&gt; Ok(value),        Err(_) =&gt; Err(ParseError::InvalidFormat),    }}fn main() {    let result = parse_positive_int(&#171;42&#187;);    match result {        Ok(value) =&gt; println!(&#171;Parsed: {}&#187;, value),        Err(err) =&gt; println!(&#171;Error code: {}&#187;, err as u32),    }}\u0418\u0437\u043c\u0435\u043d\u0451\u043d \u0442\u0438\u043f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0439 \u0441\u0440\u0435\u0437, \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0442\u0440\u043e\u043a\u0438, \u0447\u0442\u043e \u043d\u0435 \u0435\u0441\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u043e. \u0418 \u0431\u043e\u043b\u0435\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0431\u0435\u0437 \u043f\u0430\u043d\u0438\u043a. \u0415\u0449\u0451 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 as \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0430\u0434\u0451\u0436\u043d\u043e, \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438.auto result = parse_positive_int(&#171;abc&#187;);int value = result.value_or(0);\u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043d\u0430 Rust:let result = parse_positive_int(&#171;42&#187;);let value = result.unwrap_or(0);\u0420\u0430\u0437\u0432\u0435 \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u0431\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0442\u0438\u043f\u0430 &#171;auto&#187;.\u041f\u0440\u0438\u043c\u0435\u0440: \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0431\u0430\u0437\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 DTO.struct User {    int id;    std::string name;    int role_id;};enum class AppError {    UserNotFound,    AccessDenied,    DatabaseError};std::expected&lt;User, AppError&gt; load_user(int id);std::expected&lt;bool, AppError&gt; check_admin(const User&amp; user);std::expected&lt;UserDto, AppError&gt; to_dto(const User&amp; user);\/\/ \u0426\u0435\u043f\u043e\u0447\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 and_thenstd::expected&lt;UserDto, AppError&gt; get_admin_dto(int id) {    return load_user(id)        .and_then([](const User&amp; user) -&gt; std::expected&lt;User, AppError&gt; {            return check_admin(user)                .and_then([&amp;user](bool is_admin) -&gt; std::expected&lt;User, AppError&gt; {                    if (!is_admin) return std::unexpected(AppError::AccessDenied);                    return user;                });        })        .and_then(to_dto);}\u0414\u0430\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043a\u043b\u0430\u0441\u0441 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u043e\u043f\u044f\u0442\u044c \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c, \u0430 \u043f\u043e\u0442\u043e\u043c \u0443\u043b\u0443\u0447\u0448\u0438\u043c:struct User {    id: i32,    name: String,    role_id: i32,}enum AppError {    UserNotFound,    AccessDenied,    DatabaseError,}\/\/ TODOstruct UserDto;fn load_user(id: i32) -&gt; Result&lt;User, AppError&gt; {    todo!()}fn check_admin(user: &amp;User) -&gt; Result&lt;bool, AppError&gt; {    todo!()}fn to_dto(user: &amp;User) -&gt; Result&lt;UserDto, AppError&gt; {    todo!()}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    load_user(id)        .and_then(|user| {            check_admin(&amp;user).and_then(|is_admin| {                if !is_admin {                    Err(AppError::AccessDenied)                } else {                    Ok(user)                }            })        })        .and_then(|user| to_dto(&amp;user))}\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0432\u0435\u0440\u043d\u0443\u0432\u0448\u0438\u0439\u0441\u044f \u0438\u0437 load_user(). \u0412 \u0421++ \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435\u044f\u0432\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443, \u0432 Rust \u0432\u0441\u0451 \u0447\u0435\u0441\u0442\u043d\u043e \u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0443\u043a\u0430\u043c\u0438 \u0431\u0440\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0435\u0441\u0442\u0430\u0445. \u0410\u0445, \u0434\u0430, \u043d\u0438\u0433\u0434\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u0442\u0438\u043f\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 &#8212; \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0438\u0445 \u0441\u0430\u043c \u0438\u0437 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u0439.\u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0436\u0435\u043b\u0435\u0437\u043e\u0431\u0435\u0442\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u043d\u043e\u0435:fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = match load_user(id) {        Ok(user) =&gt; user,        Err(err) =&gt; return Err(err),    };    let is_admin = match check_admin(&amp;user) {        Ok(is_admin) =&gt; is_admin,        Err(err) =&gt; return Err(err),    };    if !is_admin {        return Err(AppError::AccessDenied);    }    to_dto(&amp;user)}\u041c\u0438\u043d\u0443\u0441 \u043e\u0434\u043d\u043e \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043d\u0438\u0435 (\u0438\u0437 Ok(user))! \u041d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0443\u0442\u043e\u043c\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e, \u0435\u0441\u043b\u0438 \u043d\u0430\u0434\u043e \u043b\u0438\u0448\u044c \u043f\u0435\u0440\u0435\u043a\u0438\u043d\u0443\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0432\u044b\u0448\u0435.\u0418&#8230;fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    let is_admin = check_admin(&amp;user)?;    if !is_admin {        return Err(AppError::AccessDenied);    }    to_dto(&amp;user)}\u0423\u0436\u0435 \u0447\u0438\u0449\u0435 \u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f, \u043d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u0438\u043c to_dto():\/\/ TODOstruct UserDto;impl TryFrom&lt;&amp;User&gt; for UserDto {    type Error = AppError;    fn try_from(user: &amp;User) -&gt; Result&lt;Self, Self::Error&gt; {        todo!()    }}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    let is_admin = check_admin(&amp;user)?;    if !is_admin {        return Err(AppError::AccessDenied);    }    UserDto::try_from(&amp;user)}\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u043a\u0430\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043c\u0430\u043f\u0438\u043d\u0433\u0430 \u0438\u0437 User \u0432 UserDto, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0438\u0448\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e TryFrom.\u0418 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0438\u043c check_admin, \u0445\u043e\u0442\u044f \u044d\u0442\u043e \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u0435\u043c\u043e, \u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0447\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c:const ROLE_ID_ADMIN: i32 = 1;fn check_admin(user: &amp;User) -&gt; Result&lt;(), AppError&gt; {    let is_admin = user.role_id == ROLE_ID_ADMIN;    if !is_admin {        return Err(AppError::AccessDenied);    }    Ok(())}fn get_admin_dto(id: i32) -&gt; Result&lt;UserDto, AppError&gt; {    let user = load_user(id)?;    check_admin(&amp;user)?;    UserDto::try_from(&amp;user)}\u0422\u0440\u0438 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 get_admin_dto(). Nice!\u0412\u043e\u0437\u0432\u0440\u0430\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043aYay, \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0432 \u0441\u0442\u0430\u0442\u044c\u0435!\u041e\u043f\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438:enum class DbError { ConnectionFailed, QueryFailed, RecordNotFound };enum class ServiceError { UserNotFound, InvalidState, DependencyFailed };enum class ApiError { NotFound, BadRequest, InternalError };std::expected&lt;User, DbError&gt; repo_find_user(int id);std::expected&lt;User, ServiceError&gt; service_get_user(int id) {    return repo_find_user(id)        .transform_error([](DbError db_err) -&gt; ServiceError {            switch (db_err) {                case DbError::RecordNotFound: return ServiceError::UserNotFound;                case DbError::ConnectionFailed: return ServiceError::DependencyFailed;                case DbError::QueryFailed: return ServiceError::DependencyFailed;            }            return ServiceError::DependencyFailed;        });}std::expected&lt;UserResponse, ApiError&gt; handler_get_user(int id) {    return service_get_user(id)        .transform([](const User&amp; user) { return UserResponse{user}; })        .transform_error([](ServiceError svc_err) -&gt; ApiError {            switch (svc_err) {                case ServiceError::UserNotFound: return ApiError::NotFound;                case ServiceError::InvalidState: return ApiError::BadRequest;                case ServiceError::DependencyFailed: return ApiError::InternalError;            }            return ApiError::InternalError;        });}&#187;\u0414\u0443\u0431\u043e\u0432\u0430\u044f&#187; \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0441\u0442\u0430\u0440\u0430\u044f\u0441\u044c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u0438 \u0442\u043e\u0447\u044c-\u0432-\u0442\u043e\u0447\u044c:struct User {    id: i32,    name: String,    role_id: i32,}struct UserResponse {    id: i32,    name: String,}impl UserResponse {    fn new(user: &amp;User) -&gt; Self {        Self {            id: user.id,            name: user.name.clone(),        }    }}enum DbError {    ConnectionFailed,    QueryFailed,    RecordNotFound,}enum ServiceError {    UserNotFound,    InvalidState,    DependencyFailed,}enum ApiError {    NotFound,    BadRequest,    InternalError,}fn repo_find_user(id: i32) -&gt; Result&lt;User, DbError&gt; {    todo!()}fn service_get_user(id: i32) -&gt;&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-486735","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/486735","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=486735"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/486735\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=486735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=486735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=486735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}