Тип результата

Тип результата (англ. result type) — это монадический тип, используемый в функциональном программировании для хранения возвращаемого значения или кода ошибки. Тип результата предоставляет элегантный способ обработки ошибок без использования механизма исключений: когда функция, способная завершиться с ошибкой, возвращает тип результата, программист вынужден явно рассмотреть оба случая — успех и неудачу — прежде чем получить доступ к ожидаемому результату. Это исключает возможность неверных предположений со стороны программиста.

Тип результата играет ключевую роль в обеспечении сквозной типобезопасности (англ. end-to-end type safety). Он позволяет безопасно передавать данные между различными слоями системы без риска возникновения непредсказуемых исключений, так как возможные ошибки передаются внутри контейнера и гарантированно обрабатываются на нужном этапе цепочки вызовов[1].

Примеры

  • В C++ тип результата определён стандартной библиотекой как std::expected<T, E>[2].
  • В C# и платформе .NET отсутствует стандартная реализация типа результата или размеченных объединений. Ведётся обсуждение добавления нативных размеченных объединений в язык, а на данный момент используются сторонние библиотеки, такие как OneOf и FluentResults[3][4].
  • В Elm тип результата определяется как type Result e v = Ok v | Err e[5].
  • В Haskell для этой цели по соглашению используется тип Either, определённый стандартной библиотекой как data Either a b = Left a | Right b, где a — тип ошибки, а b — тип результата[6].
  • В Java тип результата не входит в стандартную библиотеку, но реализован в сторонних библиотеках, например, в result4j (интерфейс Result<R, E>, аналогичный Result<T, E> в Rust) и vavr (интерфейс Either<L, R>, как Either a b в Haskell). Поскольку Java и Kotlin совместимы, Java может использовать тип Result из Kotlin.
  • В Kotlin тип результата определяется стандартной библиотекой как value class Result<out T>[7].
  • В OCaml тип результата представлен стандартной библиотекой как type ('a, 'b) result = Ok of 'a | Error of 'b type[8].
  • В Python тип результата отсутствует в стандартной библиотеке, но реализован в сторонних библиотеках, таких как returns и result.
  • В Rust тип результата определён стандартной библиотекой как enum Result<T, E> { Ok(T), Err(E) }[9][10].
  • В Scala стандартная библиотека также определяет тип Either[11], однако в Scala по-прежнему широко применяется более традиционная обработка исключений.
  • В Swift тип результата определён стандартной библиотекой как @frozen enum Result<Success, Failure> where Failure : Error[12].
  • В TypeScript тип результата реализуется с помощью сторонних библиотек, таких как Effect-TS и neverthrow[13].
  • В V тип результата реализован на уровне языка через возвращаемое значение !T. Например: fn my_function() !string { ... }. Обработка ошибок в V.

C++

Класс expected<T, E> использует std::unexpected() для возврата значения типа E и может возвращать T напрямую.

import std;

using std::expected;
using std::ifstream;
using std::string;
using std::stringstream;
using std::unexpected;
using std::filesystem::path;

enum class FileError {
    MissingFile,
    NoPermission,
    // другие ошибки
};

expected<string, FileError> loadConfig(const path& p) noexcept {
    if (!std::filesystem::exists(p)) {
        return unexpected(FileError::MissingFile);
    }
    ifstream config{p};
    stringstream buffer;
    if (!config.is_open()) {
        return unexpected(FileError::NoPermission);
    }
    buffer << config.rdbuf();
    config.close();
    return buffer.str();
}

int main(int argc, char* argv[]) {
    path p{/* путь к файлу */};
    if (const expected<String, FileError> s = loadConfig(p); s.has_value()) {
        std::println("Config contents: {}", s.value());
    } else if (s.error() == FileError::MissingFile) {
        std::println("Error: path {} not valid or missing!", p);
    } else if (s.error() == FileError::NoPermission) {
        std::println("Error: no permission to read file at path {}!", p);
    } else {
        std::unreachable();
    }
}

Rust

Объект типа результата имеет методы is_ok() и is_err().

const CAT_FOUND: bool = true;

fn main() {
    let result: Result<(), String> = pet_cat();
    if result.is_ok() {
        println!("Great, we could pet the cat!");
    } else {
        let error: String = result.unwrap_err();
        println!("Oh no, we couldn't pet the cat: {}", error);
    }
}

fn pet_cat() -> Result<(), String> {
    if CAT_FOUND {
        Ok(())
    } else {
        Err(String::from("The cat is nowhere to be found!"))
    }
}

В редакции Rust 2024 года были изменены правила вывода типа ! (never type): теперь приведение типов возвращается к ! вместо (), что напрямую влияет на использование оператора ? (тип успешного результата теперь выводится как !)[14]. Также ведётся работа над редизайном и стабилизацией трейта Try, определяющего поведение оператора ? (добавлены ассоциированные типы Output и Residual, а также трейт FromResidual)[15]. Кроме того, продолжает развиваться экспериментальная возможность блоков try, позволяющая использовать оператор ? внутри специальных блоков для раннего возврата ошибок в функциях, не возвращающих Result или Option[16].

Vlang

Тип Error представляет собой интерфейс для iError. Типы Option (?T) и Result (!T) в языке строго разделены, при этом интерфейс IError для пользовательских ошибок остался без изменений[17].

const cat_found = true

fn main() {
    cat_name := get_pet_cat_name() or { 
        println("Oh no, we couldn't pet the cat!")
        exit(1)
    }

    println('Great, we could pet the cat ' + cat_name)
}

fn get_pet_cat_name() !string {
    if cat_found { return 'Max' } 
    else { return error('the cat is nowhere to be found') }
}

TypeScript

В TypeScript реализация монадического типа данных (Result) осуществляется с помощью сторонних библиотек, таких как Effect-TS и neverthrow. Библиотека neverthrow предоставляет легковесный тип Result для отслеживания ошибок. Effect-TS симулирует эффекты на основе монад, используя структуру Effect<A, E, R> для отслеживания успешных результатов, ошибок и зависимостей на уровне типов. Для автоматического вывода типов в Effect-TS применяются генераторы (конструкция Effect.gen и yield*), собирающие типы из всех переданных эффектов, а также цепочки flatMap, которые связывают вычисления с сохранением информации о типах на каждом этапе[13].

Безопасное логирование

Для предотвращения утечек чувствительных данных из полей ошибок применяются методы безопасного логирования. Лучшие практики включают извлечение только необходимой информации и отказ от прямого логирования полных текстов ошибок. Вместо этого рекомендуется логировать обобщённые сообщения (например, «error detected»), а также использовать коды ошибок или идентификаторы[18].

Примечания

  1. The Result Monad. adambennett.dev (май 2020). Дата обращения: 26 августа 2026.
  2. std::expected - cppreference.com (англ.). en.cppreference.com (25 августа 2023). Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  3. Discriminated Unions: Representation of Alternative Types in .NET. thinktecture.com. Дата обращения: 26 августа 2026.
  4. csharp-error-handling-demo. github.com. Дата обращения: 26 августа 2026.
  5. Result · An Introduction to Elm. guide.elm-lang.org. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  6. Data.Either (англ.). hackage.haskell.org (22 сентября 2023). Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  7. Result - Kotlin Programming Language. kotlinlang.org. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  8. Error Handling · OCaml Tutorials. ocaml.org. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  9. std::result - Rust. doc.rust-lang.org. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  10. stdlib: Add result module · rust-lang/rust@c1092fb. github.com (29 октября 2011). Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  11. Scala Standard Library 2.13.12 - scala.util.Either. www.scala-lang.org. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  12. Result. developer.apple.com. Дата обращения: 26 августа 2026. Архивировано 9 октября 2023 года.
  13. 1 2 Effect-TS Deep Dive. yceffort.kr (февраль 2026). Дата обращения: 26 августа 2026.
  14. Never Type Fallback. The Rust Reference. Дата обращения: 26 августа 2026.
  15. RFC 3058: try_trait_v2. Rust RFCs. Дата обращения: 26 августа 2026.
  16. try_blocks. The Rust Unstable Book. Дата обращения: 26 августа 2026.
  17. Type Declarations. V Documentation. Дата обращения: 26 августа 2026.
  18. Error Leakage. Datadog Docs. Дата обращения: 26 августа 2026.

Категории