Pull to refresh

Comments 48

Как по мне, вы не с теми языками сравниваете) Я бы отважился сказать, что модель ошибок взята из Haskell. (Как впрочем и идея интерфейсов) В Haskell есть возвращаемые типы Maybe, Error, Either. Но Go это же не Haskell. Go проетировался прагматичней, Haskell для бедных)) Добавили только возможность возвращать два значения разных типов. Конструктора типов же нету)) Монаду или Maybe(type) не сделаешь. Поэтому в рамках предоставленного инструментария типов лучше решения то и не сколхозить. А предосталенный механизм вполне рационален. На мой взгляд)) Ну, generics же подвезли наконец (никому не нужный). Это, какой-никакой а конструктор типов. Думаю на генерализованных типах можно уже построить что нибудь элегантное для Error. Механизм instantiate то теперь есть...

Вышеупомянутый Тейлор, кстати, бросал в [go-nuts] лозунг -- "А кому не слабо написать монаду на Go?" Никто не справился)) А Пайк писал -- "Haskell для очкариков. Бесполезное", ну или что то вроде того))

Haskell уважаемый язык

Go всего лишь практичный

И кстати, в Go не только вербозная обработка ошибок, она ещё и без типов, что плохо. Нет exhaustive matching - компилятор не проверит что обработал все варианты:

if err == io.EOF { ... }
// забыл io.ErrUnexpectedEOF - упс

Go практичный) Haskell "уже и потому хорошо знать, что он ум в порядок приводит"(c) М. В. Ломоносов)))
Но посмотрите вакансии на hh.ru Большой ли там спрос на высоколобых хаскелеров?)))

Чтобы exhaustive matching сделать, так то вы можете столбик
if err != io.EOF { ... }
ну и

switch case //все же таки есть
pattern matching по типам нету конечно))

но
switch type .case //все же есть

Думается имеется в виду подход аля Раст, где если в match не проверил все варианты enum или не поставил catch-all - компилятор скажет фе

Раст, я нахожу overingeneering, как и scala. Мне больше нравятся установки Роба Пайка. "Вещи должны быть настолько простыми, насколько возможно. Но не проще"(c)А. Эйнштейн, и даже раньше этот посыл присылался("Не нужно плодить сущности сверх необходимого"))
Я попробовал вдупляться в Rust и Scala. И понял что это сложнее, чем моя математика. И перебор в смеси абстракций. И у меня больше сил и времени займет хорошо освоить инструмент чем алгоритмистика, математика и предметная область. Да и плюсы, С++, на мой взгляд, переусложнили) И Пайк мне нравится за идею минимально ортогональных абстракций. Haskell, по мне, ортогонален и прост)))
Scala вроде сейчас не особенно популярна. А для Раста сейчас есть зарплаты?

Вполне себе, искал в июле работу, нашёл за пару недель, получил два оффера. Вакансии на hh.ru по расту есть, и платят очень даже прилично, а взамен пишешь на очень приятном современном языке

Вот, по приколу, для смеха, поспорю с ржавым) Быструю сортировку вы легко найдете в wiki на псевдокоде. Ну или пусть, обход графа в ширину? Ну или пусть A-algorithm или муравьев или эвристику коммивояжера)) А сможете вы это быстро реализовать на Rust? Не слишком ли компилятор для этого требователен?)) На Python то только ленивый за 15 минут не реализует)) Так Python то сейчас и самый популярный/востребованный))
Да и Go то не вполне для этого. Но на Go я за 15 минут клон телеги сбацаю из стандартной библиотеки.
А растом то я пользуюсь ежедневно в firefox. А подскажите, где Rust на производстве эксплуатируется? Так то у меня ructc и rustup стоят. Пользуюсь потомучто ripgrep ежедневно) А за что нынче в production платят? Ну если не секрет) Пару то тройку лет назад, на Rust и на Go клиенты блокчейнов передовые были, но сейчас то отлюбили. А сейчас на Rust инференсеры пишут для "БольшихЯзыковыхМоделей", как модно? Или что вообще пишут?
Firefox то да, я в фанклубе))

Алгоритмы на Питоне? Это вы съели чего-то. Телегу за 15 минут сбацаете? Да ладно, не врите. Сбацаете за 14 с половиной! Максимум!

Вот прямо здесь и платят, в VK Tech: https://habr.com/ru/companies/vk/articles/832584/
А ещё платят в Wildberries, Альфа Банк, МТС, YADRO, Газпром. Много где, достаточно поискать в сети.
Насчёт стандарной библиотеки и огромного набора крейтов - тут Rust не устапает Go. Хочется найти применение для rustup? Добро пожаловать, на ваш выбор: embedded rust (stm32, esp32, nrf52, rp2040 - прекрасная поддержка популярных чипов), backend, фронтенд на WASM, CLI-инструменты, GUI-фреймворки (egui, relm4, iced, tauri, slint), можете брать no_std и писать модули ядра Linux, можете писать смарт-констракты для блокчейна или eBPF-программы, операционные системы и СУБД. Что угодно, была бы фантазия) Советую посмотреть такие крейты, как tokio, clap, axum, serde, anyhow, thiserror, tracing. Это такой, базовый набор, я бы сказал, с которым очень удобно и приятно писать различные консольные инструменты. Если уж есть желание взглянуть поближе - у Rust есть официальный учебник, который в том числе переведён на русский язык, в котором всё прекрасно рассказано.
Алгоритмы я каждый день не пишу, но если нужно - сделаю. Есть и опыт в Си, и понимание soundness, unsafe rust. Но, скорее всего, не буду изобретать велосипед, а возьму популярный крейт, хорошо протестированный и с удобным API: pathfinding, petgraph, arena-tree.

Tokio как async (Rust выбрал стратегию async/awaite) и serde как coder/decoder json(Rust выбрал стратегию reflection) конечно я читал и пробовал)) Но вы еще слили нашего соотечественника, очень талантливого парня который сделал доступный и быстрый webfreimwork)) Вроде это было на tokio async но быстро и удобно. Я сырцы то не прочел, но вроде он красавчик)) А вам, community, стыдоба)))

Дело в том, что serde – далеко не только для JSON годится :)

Но и для yaml, toml, бинарные протоколы (postcard, cbor). Т.е. у меня на микроконтроллере произвольные структуры данных, которые я с помощью serde + postcard превращаю в бинарный формат, передаю по USB с помощью postcard-rpc, и на компьютере получаю обратно исходную структуру данные. А сейчас ещё и сетевой стек пишут, ergot, очень многообещающий проект, как раз для встраиваемых систем. А использование async во встраиваемых системах, разве это не прекрасно? Какой другой язык позволит писать над микроконтроллере асинхронный код? А здесь это работает очень органично, ISR вызывают wakers, и просыпается таска. embassy.dev - прекрасный проект

Асинхроннонность rust настолько лёгковесная что можно на контроллере?

Конечно. Дело в том, что Rust не привязан к определенному асинхронному рантайму. Он даёт базовые механизмы: async/await, futures, wakers, pin, context. А асинхронный рантайм пользователь выбирает сам. Обычно это tokio или async-std, во встраиваемых системах самый развитый и мощный сейчас embassy, но есть и альтернативы. А при компиляции все асинхронные таски превращаются в конечный автомат. В embassy для создания тасок не нужна куча и количество, типы запускаемых асинхронных тасок известно на этапе компиляции. Есть другой асинхронный рантайм для микроконтроллеров, называется edge-executor, он уже требует реализацию кучи, но в замен предлагает в рантайме более гибко управлять количеством тасок, и позволяет передавать им дженерики в качестве параметров. Но он менее популярен и особого смысла / применения я для него не вижу, embassy хватает с головой.

Да, вполне.

Еще вроде, не сразу в rust решили, но сейчас в зеленых тредах решили async/awaite и возможность spawn thread системного треда. И можно делить память каналами или мьютексами. И js-script вроде так же решили)) Ложное решение, вот тут то мы суслики и победим))

А тот красавчик, о ком вы говорите, случайно не автор Actix?

но вроде он красавчик

То есть в суть драмы не вник, но выводы сделаны? Тот парень нарушал достаточно серьезные гарантии безопасности, на что ему и указали )))))))))))))

А сможете вы это быстро реализовать на Rust? Не слишком ли компилятор для этого требователен?)) На Python то только ленивый за 15 минут не реализует))

Ключевой вопрос в определении слова - быстро.

Если быстро за 15 минут то Python в победителях, с последующим выкидыванием решения на помойку.

Если быстро чтобы работало, тот тут с Rust будет тяжело тягаться GC языкам.

Так Python то сейчас и самый популярный/востребованный))

Удивительно что подавляющее большинство ключевых библиотек для этого языка написаны и пишуться на C/C++/Rust а не самом замечательном языке.

Да и Go то не вполне для этого. Но на Go я за 15 минут клон телеги сбацаю из стандартной библиотеки.

Странно сравнивать языки из разных миров с разными задачами. Можно с таким же успехом попросить сбацать на Go что-то высокопроизводительное, и окажется что для этой задачи Go станет внезапно сложным и неудобным.

Я попробовал вдупляться в Rust и Scala. И понял что это сложнее, чем моя математика. И перебор в смеси абстракций.

Со Scala соглашусь, а что в Rust сложного кроме системы заимствования?
Ну так это у всех языков без сборщика мусора здесь всегда сложности в силу самой проблемы безопасного и эффективного управления памятью.

Какие там еще сложности и абстракции? Классов нет, наследования нет, исключений нет, даже перегрузок функций нет. Ну макросы да, но никто не заставляет.

Вот зачем мне идея заимствования?)) Завтра железо будет устроено уже по другому) Вот зачем мне невнятный механизм суперпограмирования? Я не в LISP и не хочу нечитаемыех конструкций. Не хочу DomeinSpecificLanguage. Я хочу сейчас решить задачу и чтобы мне заплатили)

Вот зачем мне идея заимствования?)) Завтра железо будет устроено уже по другому)

Ваш план в том чтобы предложить заказчику дождаться светлого будущего?

Я хочу сейчас решить задачу и чтобы мне заплатили)

То есть типичный го-подход - хЪяк-хЪяк и в продакшен? А что там Роб Пайк советует?

Из такого синтаксиса невозможно понять сходу, а бросаются или нет здесь какие-то исключения, потому что в Python именно исключения являются способом возврата ошибок из того или иного метода. Мы ничего здесь не видим.

В спецификации Java Virtual Machine, например, сказано, что теоретически исключение может вылететь в любом месте кода, на буквально каждом байте байт-кода. И это надо всегда иметь в виду. Жить с этим и программировать можно. Не забывать когда нужно ставить try-catch-finally.

В 95% случаев нам не нужны ошибки как значения, а нужно лишь прервать исполнение и показать где и что произошло. Исключения с этим справляются, а простыня из if err != nil избыточна.

Ага, автор не очень разобрался в понятии исключений и ошибок в Java, увы.

Я бы тут ещё добавил про явную обработку ошибок - Java раньше по такому пути и шла - использовала по максимуму checked exception, но потом практика показала что это никому не нужно. И сейчас наоборот делают только uncheck. И кстати в java можно писать как в расте - возвращать result только с одним из значений - success с нужными объектом или error. В switch проверять. С Compile time проверкой что не забыл проверить error case.

Вроде недавно писал кто-то из разработчиков, что всё, хватит уже пытаться придумывать решения, обработка ошибок и так прекрасна(нет). Имхо го сильно развращает, он очень простой, даже я бы сказал тупой. После него, когда читаешь код на других языках, чувствуешь как скрипят шестерёнки в мозгу, забытые абстракции типа мутабельности, наследования, pattern matching. Как будто бы язык стал заложником своей простоты и поэтому перестал развиваться. Дженерики конечно частично порадовали, но на этом все. Генераторы вообще какая-то неюзабельная история в том виде в котором они представлены.

Спасибо за доклад, полезно и все в одном месте.

Мне кажется лучшее решение которое я видел - сделали в vlang

fn print_sum(a string, b string) ! {
    x := strconv.atoi(a)!
    y := strconv.atoi(b)!
    println('result: ${x+y}')
}

fn main() {
    print_sum('a', '456') or { println(err) }
}

Похоже на предложение Яна Тейлора, но тут более лаконично в целом
Мы видим что функция может вернуть ошибку так как она помечена !
Мы видим что происходит проброс ошибки на уровень выше так как при вызове функции используется тоже используется !
Если нужно обработать ошибку используется ключевое слово or
Да есть неявная переменная err но так как это механизм языка то в целом это не важно, это часть стандартного механизма обработки ошибок

Кстати механизм работает точно так же и для опциональных результатов (которые могут возвращать none)

fn do_something(s string) !string {
    if s == 'foo' {
        return 'foo'
    }
    return error('invalid string')
}

a := do_something('foo') or { 'default' }
fn do_something(s string) ?string {
    if s == 'foo' {
        return 'foo'
    }
    return none
}

a := do_something('foo') or { 'default' }

В Rust, кстати, есть возможность приделать контекст к ошибке. Есть библиотека, которая это реализует.

Фига се библиотека, первый раз вижу, обычно anyhow и thiserror используют

Моё предложение:

1) сделать "+=" для error, чтоб ошибки складывались (конкантенация)
err := One()
err += Two()

2) сделать "?=" для error, чтоб функция не вызывалась если уже есть ошибка
err := One()
err ?= Two()

+= есть через

err = errors.Join(err, Two())

Сначала напридумывал проблем, потом героически их решаем...

Какой-то океан воды, но с тремя рекламными блоками.

Panic/recover выглядит как эксепшен, работает как эксепшен, но назван по другому, и менее гибок.

Ну и обработка эксепшенов в типичном веб приложении на языке с эксепшенами сводится к перехвату эксепшенов на уровне middleware/библиотеки и преобразование их в http ответ, что имхо проще простынь из if err=.

До первого расследования инцидента.

Поэтому в golang следует каждую ошибку оборачивать и расширять контекст, чтоб по тексту было понятно что и где было вызвано, с какими параметрами и прочее. И в отличие от исключений, разработчик сам решает какие данные будут в тексте ошибки.

Это все конечно же не правда. Все выше перечисленное делается с помощью исключений очень просто.

Расследовал я инциденты уже по портянкам из стека вызовов, по которым вообще не ясно. Нет ни значений параметров, ни вызова, где именно произошла ошибка.

Одна портянка на два экрана... А если там еще многопоточка, то вообще черт ногу сломит.

Вот кстати непонятно, так ли это на самом деле. В классическом случае да, но вот при работе с многопоточкой совсем не факт что работает одинаково. Вот в С# я могу написать так

_ = Task.Run(() => DoSomething());

Тут я просто запущу таск и выйду и выполняться он будет, но если метод DoSomething() выбросит исключение, то оно нигде не обработается. Вот в этом случае кажется panic в go будет работать иначе

Обработается в TaskScheduler.UnobservedTaskException после сборки мусора, если приложение собрано в release mode.

Опять вербозно, ведь пишем тернарные операторы.

func doSomething(in Dto) error {    return in.Name == "" ? errors.New("empty name") : nil}

В Go есть тернарные операторы? Правда-правда?

не понял из текста, что это предложение их ввести. пардон. mea culpa

Вербозная статья, спасибо.

Конечно немного тумач перевербозленный артикль

Sign up to leave a comment.

Information

Website
avito.tech
Registered
Founded
2007
Employees
5,001–10,000 employees
Location
Россия
Representative
vvroschin