Сравните модели обработки ошибок в JavaScript (исключения и промисы/async-await), Go (возврат error как значения) и Rust (Result/Option): приведите короткие примеры, обсудите влияние модели на композицию кода, читабельность, обработку ресурсов и тестируемость, и предложите рекомендации для публичного API библиотеки

26 Сен в 20:30
13 +1
0
Ответы
1

Кратко: у каждой модели свои сильные стороны и компромиссы. Ниже — сравнение, короткие примеры и практические рекомендации для публичного API библиотеки.

1) JavaScript — исключения и Promises/async-await

Модель: синхронно бросают исключения throwthrowthrow, в асинхронном коде — Promise reject; async/await делает промисы похожими на синхронный код, но тип ошибок не контролируется языком (любой объект может быть "брошен").Преимущества: простая видимая non−localnon-localnonlocal передача ошибки; async/await делает чтение и композицию асинхронного кода удобной.Минусы: отсутствие статической типизации ошибок; легко пропустить обработку; смешение контроля потока и ошибок.

Примеры:

Синхронно:
function parseJsonsss {
if !s!s!s throw new Error"empty""empty""empty";
return JSON.parsesss;
}

try {
const obj = parseJsonsss;
} catch errerrerr {
// обработка
}

- Асинхронно async/awaitasync/awaitasync/await:

async function fetchDataurlurlurl {
const res = await fetchurlurlurl;
if !res.ok!res.ok!res.ok throw new Error("fetch failed");
return res.json;
}

try {
const data = await fetchDataurlurlurl;
} catch errerrerr {
// обработка
}

- Промисы цепочки,композицияцепочки, композицияцепочки,композиция:

Promise.all[fetchA(),fetchB()][fetchA(), fetchB()][fetchA(),fetchB()] .then(([a,b]) => ...)
.catch(err => ...);

2) Go — возвращаем error как значение
- Модель: функции возвращают value,errorvalue, errorvalue,error. Ошибка — обычное значение, которое нужно явно проверить iferr!=nilif err != niliferr!=nil. Паники используются редко для критических, не-обрабатываемых ситуаций.
- Преимущества: явность, простота трассировки, легко написать код, надежно контролировать ветви ошибок; defer для гарантированной очистки ресурсов.
- Минусы: много шаблонного кода проверкиошибокпроверки ошибокпроверкиошибок; композиция цепочек действий требует явного пробрасывания ошибок; отсутствие богатой системы типов ошибок ноестьwrappingиerrors.Is/Asно есть wrapping и errors.Is/Asноестьwrappingиerrors.Is/As.
Пример:

func ReadFilepathstringpath stringpathstring []byte,error[]byte, error[]byte,error {
f, err := os.Openpathpathpath if err != nil {
return nil, err
}
defer f.Close return io.ReadAllfff }

data, err := ReadFile("a.txt")
if err != nil {
// обработка
}

3) Rust — Result и Option, возможен panic для критических ошибок
- Модель: ошибки — часть типов: Result<T, E> или Option<T>. Пропагируются явным ? оператором или pattern matching; ошибки типизированы EможетбытьenumE может быть enumEможетбытьenum.
- Преимущества: статическая типизация ошибок, безопасная композиция, лаконичная передача ошибок через ?, ресурсы управляются RAII/Drop очисткагарантированапривыходеизблокаочистка гарантирована при выходе из блокаочисткагарантированапривыходеизблока.
- Минусы: требуется проектирование типов ошибок и преобразований From/IntoFrom/IntoFrom/Into, иногда verbose для очень простых случаев; learning curve.
Пример:

fn read_file(path: &str) -> Result<String, std::io::Error> {
let s = std::fs::read_to_stringpathpathpath?; // ? пробрасывает ошибку
Oksss }

match read_file("a.txt") {
Oktexttexttext => { / ok / }
Erreee => { / обработка / }
}

Option:

fn find_userid:u32id: u32id:u32 -> Option { ... }
if let Someuuu = find_userididid { ... } else { ... }


Влияние модели на композицию кода
- JS:
- Исключения дают "non-local" выход — удобно, но делает явную композицию труднее отслеживаемой.
- Promises позволяют композицию .then/.catch,Promise.all.then/.catch, Promise.all.then/.catch,Promise.all, но обработка ошибок типово не проверяема по типу.
- async/await упрощает последовательную композицию, но нужно помнить await и возможные забытые catch.
- Go:
- Явное возвращение ошибок упрощает понимание потока и контроль — композиция простая, но вербозная: надо явно проверять и пробрасывать ошибки во многих местах.
- Для сложной композиции используются helper-функции/паттерны wrapper,middlewarewrapper, middlewarewrapper,middleware.
- Rust:
- Result + ? дают очень удобную композицию: можно писать последовательный код, не захламляя его проверками.
- Комбинаторы map,andthenmap, and_thenmap,andt hen и pattern matching позволяют выразительную композицию обработок.
Влияние на читабельность
- JS: при хорошем использовании async/await код читаем; но неявность ошибок и разнообразие форм string,Error,objectstring, Error, objectstring,Error,object ухудшают понимание контракта.
- Go: явность делает причины ошибок очевидными, но много boilerplate.
- Rust: при грамотном дизайне типов ошибок код и контракт очень читаемы; синтаксис ? уменьшает шум.
Обработка ресурсов cleanupcleanupcleanup - JS:
- try/finally и finally в async/await используются для очистки; в промисах нужен .finally.
- GC управляет памятью, но внешние ресурсы надо освобождать вручную.
- Go:
- defer — сильный и надёжный инструмент для очистки внезависимостиотраннегоreturnвне зависимости от раннего returnвнезависимостиотраннегоreturn.
- Rust:
- RAII/Drop — ресурсы автоматически освобождаются при выходе из области видимости; это самый надёжный способ управления ресурсами.
Тестируемость
- JS:
- Тестирование ошибок затрудняется отсутствием контрактов типов ошибок; нужно проверять по классу Error, message или пользовательской метке.
- Легко писать интеграционные тесты с промисами/async-await.
- Go:
- Явные ошибки легко мокать и проверять; можно определить sentinel errors и сравнивать через errors.Is.
- Rust:
- Типизированные ошибки упрощают написание unit-тестов; можно моделировать варианты Result/Option в тестах прямо на уровне типов.
Рекомендации для публичного API библиотеки
общиепринципыобщие принципыобщиепринципы - Документируйте все возможные ошибки классы/коды/семантикуклассы/коды/семантикуклассы/коды/семантику и указывайте, какие ошибки считаются временными/повторимыми.
- Выбирайте модель ошибок, идиоматичную для языка неизобретайтечужиепаттерныне изобретайте чужие паттернынеизобретайтечужиепаттерны:
- JS: возвращайте Promise asyncfunctionsasync functionsasyncfunctions и бросайте/отклоняйте с Error-подклассами; не бросайте примитивы; экспортируйте собственные Error-классы или коды err.codeerr.codeerr.code для машинной обработки.
- Go: возвращайте T,errorT, errorT,error; не используйте panic для обычных ошибок; предоставьте хорошо именованные ошибки varErrNotFound=errors.New(...)var ErrNotFound = errors.New(...)varErrNotFound=errors.New(...) и используйте wrapping fmt.Errorf("fmt.Errorf("%w", err)fmt.Errorf(" чтобы сохранить трассировку; поддерживайте context.Context для отмены/таймаутов.
- Rust: возвращайте Result<T, E> с E — понятный enum ошибки; реализуйте std::error::Error + Display; используйте thiserror/snafu для удобства; для простоты можно в высокоуровневых приложениях использовать anyhow, но публичный API лучше с явным enum.
- Категоризация ошибок: предоставляйте классификацию например,NotFound,InvalidArgument,Timeout,Temporaryнапример, NotFound, InvalidArgument, Timeout, Temporaryнапример,NotFound,InvalidArgument,Timeout,Temporary и/или машинные коды, чтобы пользователи могли программно принимать решения retry,fallbackretry, fallbackretry,fallback.
- Не смешивайте подходы: в одной библиотеке лучше единообразие например,вJSневозвращатьиногдапромис,иногдаколбэкнапример, в JS не возвращать иногда промис, иногда колбэкнапример,вJSневозвращатьиногдапромис,иногдаколбэк.
- Ресурсная безопасность: обеспечьте гарантированный cleanup вJS—документацияпроfinally,вGo—предлагаемdefer,вRust—опишитеownershipиDropв JS — документация про finally, в Go — предлагаем defer, в Rust — опишите ownership и DropвJSдокументацияпроfinally,вGoпредлагаемdefer,вRustопишитеownershipиDrop.
- Совместимость и миграция: если библиотека может быть использована из разных контекстов sync/asyncsync/asyncsync/async, документируйте поведение, и по возможности предоставьте только асинхронный API вJSв JSвJS или синхронный вGo/Rustв Go/RustвGo/Rust как это ожидаемо.
- Тестируемость: проектируйте ошибки так, чтобы в тестах было легко симулировать различные ветви mock−объекты,возвратспециальныхerror−кодов,featureflagsmock-объекты, возврат специальных error-кодов, feature flagsmockобъекты,возвратспециальныхerrorкодов,featureflags.
Конкретные советы по языкам для публичного API
- JavaScript:
- API должен возвращать Promise илибытьasyncили быть asyncилибытьasync. Все ошибки — экземпляры Error предпочтительнособственныеклассы,напримерMyLibErrorсполямиcode,metaпредпочтительно собственные классы, например MyLibError с полями code, metaпредпочтительнособственныеклассы,напримерMyLibErrorсполямиcode,meta.
- Документируйте коды ошибок и условия бросания.
- Пример: export class NotFoundError extends Error { constructormsgmsgmsg{ supermsgmsgmsg; this.code='NOT_FOUND'; } }
- Go:
- Возвращайте T,errorT, errorT,error. Определите пакетовую ошибку-константу для часто проверяемых случаев ErrNotFoundErrNotFoundErrNotFound.
- Используйте errors.Is/errors.As для распознавания обёрнутых ошибок.
- Не паниковать при обычных ошибках; используйте context для отмены.
- Rust:
- Возвращайте Result<T, ErrorEnum>. Сделайте ErrorEnum исчерпывающим и документируйте варианты.
- Реализуйте Display и std::error::Error; предоставьте From<...> для удобных конверсий.
- Для клиентов, которым важна удобность, можно в дополнение иметь модуль с высокоуровневым типом anyhowanyhowanyhow — но публичный API лучше с явными ошибками.
Короткое резюме
- Если язык динамический JSJSJS — используйте стандартную модель throw/rejectthrow/rejectthrow/reject но централизуйте ошибки в Error-подклассах с кодами/метаданными.
- Если язык у вас явного error-as-value GoGoGo — следуйте идиоме: возвращайте error, используйте wrapping/Is и defer для ресурсов.
- Если язык типизированный как Rust — отдавайте предпочтение Result/Option с хорошо продуманными типами ошибок и ? для удобства композиции.
Если нужно, могу:
- Предложить шаблон скелетскелетскелет enum-ошибок для Rust или пример Error-классов и кодов для JS;
- Показать пример API библиотеки с конкретной схемой кодов ошибок и обработкой в типичном consumer-коде.
26 Сен в 21:21
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир