На нынешней работе приходится использовать Go. Я хорошо познакомился с этим языком. Мне он не нравится, и меня озадачивает его популярность.

Эргономика разработки

Никогда не встречал языка, настолько открыто противостоящего удобству для разработчика. К примеру, Роб Пайк неоднократно и открыто враждебно относился к любому обсуждению подсветки синтаксиса на Go Playground. В ответ на разумно сформулированные вопросы пользователей его публичные ответы отсвечивали пренебрежением и неуважением:

Gofmt написан специально чтобы уменьшить количество бессмысленных дискуссий о форматировании кода, что отлично удалось. К сожалению, это никак не повлияло на количество бессмысленных дискуссий о подсветке синтаксиса или, как я предпочитаю её называть, spitzensparken blinkelichtzen.

И снова в ветке 2012 Go-Nuts:

Подсветка синтаксиса — для маленьких. В детстве меня учили арифметике на цветных палочках. Сейчас я вырос и использую чёрно-белые цифры.

Ясно, что из знакомых Роба никто не страдает от синестезии, дислексии или плохого зрения. Из-за его позиции официальный сайт и документация Go до сих пор остаются без подсветки синтаксиса.

Группа разработчиков Go не ограничивается Пайком, но остальные всячески поддерживают его отношение к эргономике. В обсуждении типов union/sum пользователь ianlancetaylor отклонил запрос, конкретно определяющий преимущество эргономики, как слишком незначительный и не достойный внимания:

Это обсуждалось несколько раз в прошлом, в том числе до открытого релиза. Тогда мы пришли к мнению, что типы sum не особо расширяют интерфейсные типы. Если разобраться, в итоге всё сводится к тому, что компилятор проверяет, что вы заполнили все случаи переключения типов. Это довольно небольшое преимущество, чтобы менять язык.

Такое отношение расходится с мнением о типах union в других языках. В 2000 году JWZ критиковал Java:

Также я думаю, что для моделирования enum и :keywords используются довольно ламерские идиомы. (Например, компилятор не имеет возможности выдать спасительное предупреждение, что «`enumeration value x', не обработано в switch»).

Команда Java приняла такую критику близко к сердцу, и теперь Java может выдать это предупреждение для операторов множественного выбора по типам перечисления. Другие языки — в том числе и современные языки, такие как Rust, Scala, Elixir и им подобные, а также собственный прямой предок Go, язык C — тоже выдают предупреждения, где это возможно. Очевидно, что такие предупреждения полезны, но для команды Go комфорт разработчика недостаточно важен и не заслуживает рассмотрения.

Политика


Нет, я не про интриги в списках рассылках и на встречах. Вопрос более глубокий и интересный.

Как и любой язык, Go является инструментом политики. Он воплощает определённый набор представлений, как должно быть написано и организовано программное обеспечение. В случае Go воплощается чрезвычайно жёсткая кастовая иерархия «опытных программистов» и «неквалифицированных программистов», навязанная самим языком.

Со стороны неквалифицированных программистов язык запрещает функции, которые считаются «слишком продвинутыми». Здесь нет универсальных дженериков, нельзя писать функции более высокого порядка, которые обобщают более чем один конкретный тип, и чрезвычайно строгие правила о наличии запятых, неиспользуемых символов и других недостатках, которые могут возникнуть в обычном коде. Программисты Go живут в мире, который ещё более ограниченный, чем Java 1.4.

Опытным программистам доверяют эти функции — и они могут отдавать системы на таком коде коллегам по обе стороны барьера. Реализация языка содержит родовые функции, которые нельзя использовать на практике, и с отношениями типов, которые язык просто не способен выразить. Это мир, в котором живут разработчики программ на Go.

Не знаю как внутри Google, но за её пределами это негласное политическое разделение программистов на «благонадежных» и «неблагонадёжных» лежит в основе многих рассуждений о языке.

Пакеты и распространение кода


Пакетный менеджер go get разочаровывает своим отказом от ответственности. Границы пакетов — это место для коммуникации между разработчиками, а команда Go принципиально отказывается помочь.

Я могу уважать позицию команды Go, которая заключается в том, что это не их проблема, но тут их действия невыгодно отличаются от других основных языков. Достаточно вспомнить катастрофическую историю попыток управления пакетами для библиотек C и посмотреть на Autotools — пример того, как долго может сохраняться столь бедственное положение. С учётом этого весьма удивительно наблюдать, что команда разработчиков языка в 21 веке умывает руки в такой ситуации.

GOPATH


Монолитный путь для всех исходников неизбежно приводит к конфликтам версий между зависимостями. Настройка vendor частично решает эту проблему за счёт существенного раздутия репозитория и нетривиального изменения связей, которые могут привести к ошибкам, если в одном приложении остались ссылки на «вендорскую» и «невендорскую» копии одной и той же библиотеки.

Опять же, ответ команды Go «не наша проблема» разочаровывает и расстраивает.

Обработка ошибок в Go


Стандартный подход Go к действиям, которые могут завершиться ошибкой, включает в себя возврат нескольких значений (не многокомпонентного объекта; в Go таких нет) с типом последнего значения error в виде интерфейса, где значение nil означает «ошибки нет».

Поскольку это негласное соглашение, оно не представлено в системе типов Go. Не существует обобщённого типа, представляющего результат потенциально ошибочной операции, над которым можно писать полезные объединяющие функции. Более того, он не всегда соблюдается: ничто, кроме здравого смысла, не мешает программисту вернуть error в каком-то ином виде, например, в середине последовательности возвращаемых значений или в начале — поэтому методы обработки ошибок тоже чреваты проблемами.

В Go невозможно составить потенциально ошибочные операции более лаконичным способом, чем что-то такое:

a, err := fallibleOperationA()
if err != nil {
    return nil, err
}

b, err := fallibleOperationB(a)
if err != nil {
    return nil, err
}

return b, nil

В других языках это можно сформулировать как

a = fallibleOperationA()
b = fallibleOperationB(a)
return b

в языках с исключениями или

return fallibleOperationA()
    .then(a => fallibleOperationB(a))
    .result()

в языках с соответствующими абстракциями.

Это существенная разница, особенно если у вас длинные последовательности таких операций (даже при поддержке редактора, который поддерживает генерацию ветвей). Приходится тратить лишнее время на кодирование и дополнительные когнитивные усилия на чтение кода. Руководства по стилю помогают, но смешивание стилей только усугубляет ситуацию. Как пример:

a, err := fallibleOperationA()
if err != nil {
    return nil, err
}

if err := fallibleOperationB(a); err != nil {
    return nil, err
}

c, err := fallibleOperationC(a)
if err != nil {
    return nil, err
}

fallibleOperationD(a, c)

return fallibleOperationE()

Да поможет вам Бог сделать вложение или что-то кроме передачи ошибки обратно на стек.

Комментарии (273)


  1. DrAndyHunter
    26.12.2018 14:14
    +6

    Модули же завезли с 1.11 версии. В дальнейшем и от GOPATH хотят избавиться


    1. gudvinr
      26.12.2018 23:22

      С этим есть нюансы.


      • Некоторые утилиты для go generate и средства работы с кодом для IDE до сих пор в некоторых случаях работают не так хорошо, как с пакетами в GOPATH, а то и вовсе не работают. С этим постепенно справляются, но тем не менее.
      • Не слишком понятно, почему требуется "v" в начале имени тега.
      • Возможно, только у меня, но принудительный вендоринг иногда выкидывает поддиректории из репозиториев, из которых подключается несколько библиотек (например, если из github.com/a/b импортируются /c и /d, в папке vendor может быть github.com/a/b/c, то /d не будет), происходило с gogo-proto

      В целом удобно, т.к. гвоздями прибитая структура пакетов не есть хорошо, но GOPATH удобный, когда через go get устанавливаются исполняемые файлы. И поломанные утилиты вроде stringer печалят конечно.


  1. serge-phi
    26.12.2018 14:30
    +4

    В вышеприведенном коде нет обработки ошибок, только их переброс наверх. Подход Go подталкивает к обработке ошибок, а не на отсылку их выше в надежде, что кто-нибудь разберется.


    1. alexs0ff
      26.12.2018 14:50
      +1

      Есть понятие ошибки и есть понятие исключительных ситуаций.
      Их следует четко различать:
      Ошибки логически ожидаются, и их как правило стараются обработать тут же.
      Exception`ы возникают в ситуациях, когда нормальная работа какой-то подсистемы не возможна и следует «оповестить» об этом вышестоящий код.

      Нормально, когда в системе используются два этих подхода одновременно, ненормально, когда один подход заменяют на другой и путают их.

      Конечно можно обойтись одними ошибками (в виде кодов возврата и т.д.), но оверхеда становится очень много.


      1. serge-phi
        26.12.2018 15:02
        -1

        Когда нормальная работа подсистемы невозможна, то она должна быть отключена. Для этого нужно ее соответствующим образом запрограммировать (в виде процесса или потока). А исключительные ситуации удобны для разработки библиотек, а в обычном коде ошибки надо обрабатывать, а не перебрасывать их «вышестоящему коду».


        1. balexa
          27.12.2018 11:27
          +5

          а в обычном коде ошибки надо обрабатывать

          Далеко не все ошибки пришедшие «снизу» могут быть обработаны на данном слое абстракции. Часто их надо пробрасывать наверх.

          И в целом подход «если вам не нравится что-то в нашем божественном языке — вы просто неопытный быдлокодер» — он малоконструктивен.


          1. serge-phi
            27.12.2018 14:14
            -2

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


            1. TheShock
              28.12.2018 02:09

              Слишком глуп, чтобы придумать вменяемый аргумент? Аппелируй к возрасту!


              1. serge-phi
                28.12.2018 10:49

                Смысл фразы во второй части предложения, а не в возрасте.


                1. TheShock
                  28.12.2018 17:56

                  Ааа! Вы о аппеляции к авторитету? Тоже не лучший аргумент


                  1. serge-phi
                    29.12.2018 10:22

                    Глупое предположение. Как можно апеллировать к авторитету в интернете? Ключевое слово «промышленный».
                    Такой софт живет десятки лет и переживает смену многих программистов. Для такого софта тотальная огороженность и упрощенность Go очень подходит.


      1. DexterHD
        26.12.2018 15:08
        +1

        И в go используются и реализованы оба подхода… Ошибки это просто значения а для исключительных ситуаций есть `panic, defer, recover`


        1. powerman
          26.12.2018 19:46
          -1

          Не совсем так. Технически panic можно для этого использовать, но он предназначен для другого: для багов. Если в коде возникла ситуация, которая не должна была возникнуть при штатной работе (и ошибки, которые вполне могут возникать штатно, не важно насколько они критичны и может ли приложение продолжать работу при их возникновении сюда не входят), т.е. наличие такой ошибки является признаком наличия в коде бага — вот тогда и только тогда нужно использовать panic. Помимо этого есть только один допустимый способ использования panic а-ля исключения, т.е. для управления потоком выполнения — внутри пакета, с обязательным перехватом через recover и преобразованием в обычную ошибку на границе пакета, чтобы из его публичного интерфейса эти паники не вылетали.


          1. DexterHD
            26.12.2018 21:21
            +3

            Я ничего и не говорил про как и что правильно использовать. Я лишь пояснил автору комментария выше что в Go есть механизмы для обработки разного типа ошибок. Т.е. есть механизмы и для обработки обычных ошибок и для обработки ошибок которые «не должны произойти».


      1. PsyHaSTe
        27.12.2018 15:17

        del


      1. vanxant
        27.12.2018 17:47

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


    1. PsyHaSTe
      27.12.2018 15:17

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


      1. qrKot
        27.12.2018 15:27

        Фейлить старт, это же очевидно.


        1. PsyHaSTe
          27.12.2018 15:30

          Как именно? Возвращать nil вместо объекта конфигурации? А что пользователю с этим делать? Он может хочет узнать, почему файл не читается.


          1. isden
            27.12.2018 15:45

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


            1. PsyHaSTe
              27.12.2018 16:27

              Уронить приложение из-за невозможности прочитать файл? Пользователи скажут спасибо


              1. isden
                27.12.2018 16:29

                Паника != уронить. Паника — это штатный механизм, который можно обработать.
                И что вы предлагаете делать, если конфиг не читается? Загружать пустой дефолтный и пытаться работать? Ну вариант конечно, но на мой взгляд совсем не очень. Я бы лично в такой ситуации выкидывал панику с записью в лог (или консоль если это консольное приложение).


                1. PsyHaSTe
                  27.12.2018 16:33

                  Паника != уронить

                  По сути — равно. Глобальный хэндлер паники, конечно же, можно настроить, но как правило он просто залоггирует что-то (потому что контекста сделать что-то полезное у него тупо нет). И если после этого он не упадет и оставит программу в потенциально некорректном состоянии, то это даже хуже, чем молчаливые падения.

                  Что касается вопроса «что же я предлагаю», я ответил чуть ниже: habr.com/post/434446/#comment_19557444


                  1. isden
                    27.12.2018 17:21

                    Нет, «уронить» — это сделать так, чтобы программа упала, совсем (ну, как грубый пример, преднамеренно написать код так, чтобы программа убивалась OOM killer в определенный момент).
                    Паника — это что-то вроде глобального исключения, которое можно поймать и обработать. Штатный механизм для исключительных ситуаций.

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

                    И что же может сделать вызывающий код, в обсуждаемом случае? Выкинуть панику? :)


                    1. PsyHaSTe
                      27.12.2018 18:16
                      +1

                      Паника — это что-то вроде глобального исключения, которое можно поймать и обработать. Штатный механизм для исключительных ситуаций.

                      Панику нельзя обработать кроме как залоггировать и упасть, о чем я уже говорил. Попытка продолжить выполнение после паники — UB.


                      И что же может сделать вызывающий код, в обсуждаемом случае? Выкинуть панику? :)

                      Например сделать MessageBox::show(error.GetText()). Или взять конфиг по-умолчанию. Или еще что-нибудь. Вызывающий код знает, что с этим сделать. А может запаниковать, да. Он пусть решает. Хорошая статья в тему.


                      1. isden
                        27.12.2018 18:22

                        > Панику нельзя обработать кроме как залоггировать и упасть, о чем я уже говорил. Попытка продолжить выполнение после паники — UB.

                        When a function encounters a panic, its execution is stopped, any deferred functions are executed and then the control returns to its caller. This process continues until all the functions of the current goroutine have returned at which point the program prints the panic message, followed by the stack trace and then terminates.

                        Recover is useful only when called inside deferred functions. Executing a call to recover inside a deferred function stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function, it will not stop a panicking sequence.


                        > Например сделать MessageBox::show(error.GetText()). Или взять конфиг по-умолчанию.

                        А почему это нельзя сделать в том месте, где и произошла ошибка?
                        Не смогли прочитать файл конфига? Заполняем там же структуру дефолтными значениями например.


                        1. PsyHaSTe
                          27.12.2018 18:39

                          И все же: у вас остается программа в неконсистентном состоянии. Если у нас был код типа


                          db.Update(reading: true);
                          var file = ReadFromFile();
                          db.Update(reading: false, filename: file.name);

                          Если ReadFromFile() паникует, мы не выполняем последнюю строчку и остаемся в неконсистентном состоянии. Если же выполняем, то чему будет равен file.name?


                          А почему это нельзя сделать в том месте, где и произошла ошибка?
                          Не смогли прочитать файл конфига? Заполняем там же структуру дефолтными значениями например.

                          библиотека, работающая с JSON, которая имеет зависимость на GTK чтобы показывать сообщение об ошибке в мессадж боксе? Ну даже не знаю, что про это можно сказать :)


                          Заполняем там же структуру дефолтными значениями например.

                          Ну это и есть "молчаливое" поведение, о чем в статье выше расписано. Вы читали?


                          Если это рекомендуемое поведение го, то спасибо, не надо такого.


                          1. isden
                            27.12.2018 19:00

                            > Если у нас был код типа

                            А зачем так делать? О_О
                            Не нужно так делать никогда, запись в базу должна быть атомарной, а не размазанной вот так.
                            Прочитали файл — пишем в базу. Не прочитали — не пишем вообще ничего.

                            > библиотека, работающая с JSON, которая имеет зависимость на GTK чтобы показывать сообщение об ошибке в мессадж боксе? Ну даже не знаю, что про это можно сказать :)

                            Ну всяких примеров можно много разных насочинять. Я про «действовать по ситуации», по-моему, пару раз уже тут говорил.


                            1. TheShock
                              27.12.2018 19:02

                              Деньги упали на счет клиента — пишем в базу.
                              Не упали — не пишем ничего.

                              Правильно я вас понял?


                              1. isden
                                27.12.2018 19:04

                                Что ответить на ваш вопрос, опишите сначала полностью весь кейс.
                                Если вам нужно записать что-то из файла в базу, то очевидно что вам нужно прежде всего открыть и прочитать этот файл перед записью, а не в процессе.


                                1. 0xd34df00d
                                  28.12.2018 22:18

                                  А вот в этом и дело: нету всего кейса. Вы пишете библиотеку, и про весь кейс будут знать только её пользователи.

                                  Поэтому заполнять дефолтовыми значениями или показывать месседжбоксы несколько нереюзабельно.


                                  1. isden
                                    29.12.2018 00:42

                                    > А вот в этом и дело: нету всего кейса. Вы пишете библиотеку, и про весь кейс будут знать только её пользователи.

                                    Ну если нету кейса (== нет ТЗ, как минимум в голове разработчика), то о какой разработке может идти речь?
                                    Чет мне сейчас тот анекдот про японскую пилу и суровых сибирских лесорубов вспомнился.


                                    1. 0xd34df00d
                                      29.12.2018 02:18
                                      +1

                                      Нету всего кейса. Неизвестно, кто, откуда и в каком контексте будет вас дёргать.


                                      У меня не далее как на этой неделе был пример, когда некоторый обработчик некоторого потока работал в двух режимах: онлайн и replay. В первом невозможность при инициализации загрузить вспомогательные данные означала необходимость попробовать с предыдущей версией тех же данных (после дополнительных проверок их достаточной актуальности), во втором — сразу экзепшон и крашиться.


                                      И я даже не библиотечный код писал. Просто два принципиально разных кейса для одного и того же куска кода.


                                      1. isden
                                        29.12.2018 10:07

                                        > В первом невозможность при инициализации загрузить вспомогательные данные означала необходимость попробовать с предыдущей версией тех же данных (после дополнительных проверок их достаточной актуальности), во втором — сразу экзепшон и крашиться.

                                        Ну так вот же у вас вполне четкие требования уже есть. Пишете под них, а не универсальный сферический хандлер всего что только может быть в принципе.

                                        Возвращаясь в контекст го — вот типовой пример — golang.org/pkg/net/http/#Server — инициализируем структурой с настройками. Если что-то не так — то ListenAndServe вернет ошибку, и типовая сферическая инициализация выглядит как-то так:

                                        s := &http.Server{
                                        	Addr:           ":8080",
                                        	Handler:        myHandler,
                                        	ReadTimeout:    10 * time.Second,
                                        	WriteTimeout:   10 * time.Second,
                                        	MaxHeaderBytes: 1 << 20,
                                        }
                                        log.Fatal(s.ListenAndServe())
                                        


                                        Т.е. оно не думает что и откуда прочитать для получения настроек, оно просто принимает структуру. А где и как вы её получили — дело исключительно вашего приложения.


                            1. PsyHaSTe
                              27.12.2018 19:48

                              А зачем так делать? О_О
                              Не нужно так делать никогда, запись в базу должна быть атомарной, а не размазанной вот так.

                              Ну а как тут сделаешь атомарной, если там паника. Даже если какой-нибудь try finally блок делать, он по идее не выполнится.

                              Прочитали файл — пишем в базу. Не прочитали — не пишем вообще ничего.

                              Так смысл именно про стейт. Мы взяли, обновили стейт, пошли читать файл, после этого стейт нужно снова обновить. А тут паника. Что делать?

                              Ну всяких примеров можно много разных насочинять. Я про «действовать по ситуации», по-моему, пару раз уже тут говорил.

                              Ну я вот спрашиваю про конкретную ситуацию, что делать предлагается. Ситуацию вы вполне знаете, требования простые: чтобы я мог прочитать конфиг, и сделать кто-то, если с конфигом что-то не в порядке.


                              1. isden
                                27.12.2018 20:37
                                -1

                                > Ну а как тут сделаешь атомарной, если там паника. Даже если какой-нибудь try finally блок делать, он по идее не выполнится.

                                Открыли файл, прочитали его (или его блок если файл большой), записали в базу.
                                Если хреново открыли/прочитали, записи в базу не будет.
                                Ну в самом деле, это такие элементарные вещи, что как-то даже странно не знать их.

                                > Так смысл именно про стейт. Мы взяли, обновили стейт, пошли читать файл, после этого стейт нужно снова обновить. А тут паника. Что делать?

                                Какой стейт и зачем? Опишите кейс более подробно.

                                > Ситуацию вы вполне знаете, требования простые: чтобы я мог прочитать конфиг, и сделать кто-то, если с конфигом что-то не в порядке.

                                Я бы сделал именно так как уже говорил — ошибка в лог/консоль и аварийное завершение работы. Плюс в CLI еще, возможно, добавил бы опцию для генерации дефолтного конфига. Но запуск с нечитаемым/испорченным конфигом — это аварийная ситуация.
                                В ситуации чисто десктопного GUI приложения — да, выкидывать окошко с ошибкой. Тут уже можно поиграть на тему независимого error helper'а, запускаемого при аварийном завершении основной программы (или на тему засылки во что-то, что само покажет окошко с мессагой, ну dbus например).


                                1. PsyHaSTe
                                  27.12.2018 21:01

                                  Открыли файл, прочитали его (или его блок если файл большой), записали в базу.
                                  Если хреново открыли/прочитали, записи в базу не будет.
                                  Ну в самом деле, это такие элементарные вещи, что как-то даже странно не знать их.

                                  Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».

                                  Какой стейт и зачем? Опишите кейс более подробно.

                                  В данном случае БД. Например, у нас несколько инстансов, и если один инстанс начал читать файл, то он выставляет флажок, чтобы другие инстансы понимали, что файл читается, и не трогали его.

                                  Я бы сделал именно так как уже говорил — ошибка в лог/консоль и аварийное завершение работы.

                                  Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?


                                  1. isden
                                    27.12.2018 21:50

                                    > Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».

                                    И почему это может вызвать проблемы?
                                    Ставим флажок «я начал читать файл». Открываем — не открывается — по деферу сбрасываем флажок. В базу ничего не попадает.

                                    > В данном случае БД. Например, у нас несколько инстансов, и если один инстанс начал читать файл, то он выставляет флажок, чтобы другие инстансы понимали, что файл читается, и не трогали его.

                                    Посмотрите вот тут — github.com/minio/minio/blob/master/docs/shared-backend/DESIGN.md#architecture — очень неплохой пример как надо делать такие вещи.

                                    > Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?

                                    Я вас где-то заставляю делать так? Делайте как вам нужно.


                                    1. PsyHaSTe
                                      27.12.2018 21:51

                                      И почему это может вызвать проблемы?
                                      Ставим флажок «я начал читать файл». Открываем — не открывается" по деферу сбрасываем флажок. В базу ничего не попадает.

                                      Дефер будет вызван в случае паники?


                                      1. isden
                                        27.12.2018 21:53

                                        1. PsyHaSTe
                                          27.12.2018 21:54

                                          Ок, а что будет в случае другой паники во время обработки этого дефера?


                                          1. isden
                                            27.12.2018 21:57

                                            А вы погуглить не пробовали? :)
                                            По первой же ссылке все написано.


                                            1. PsyHaSTe
                                              27.12.2018 23:17

                                              Любопытно. Спасибо


                                          1. powerman
                                            27.12.2018 22:01

                                            Вторая паника будет добавлена к описанию первой, а в остальном никакой разницы, одна паника или две: https://play.golang.org/p/Of3KiMh4Tdg — пока не вызвали recover() паника продолжит раскручивать стек и выполнять defer-ы.


                                      1. powerman
                                        27.12.2018 21:55

                                        Да, конечно, defer вызывается при любом выходе из функции, хоть return хоть panic.


                                      1. qrKot
                                        28.12.2018 09:21

                                        Да.


                                  1. qrKot
                                    28.12.2018 09:20

                                    Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».


                                    Ну, т.е., если по человечески: мы должны открыть файл, прочитать файл, закрыть файл, и уже после этого начать писать в базу?

                                    В данном случае БД. Например, у нас несколько инстансов, и если один инстанс начал читать файл, то он выставляет флажок, чтобы другие инстансы понимали, что файл читается, и не трогали его.


                                    Чуть раньше было про конфиг, ага. Ну да ладно, Г-дь вам судья.

                                    Понимаете, тут все несколько проще: один инстанс открывает файл на чтение и… никаких флажков, оно блокируется на уровне ФС, ресурс занят. Читает файл, закрывает файл (освобождает ресурс). Все.

                                    Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?


                                    В случае с поломанным конфигом хотеть вы можете много чего, а нужно вам всегда тупо налить логов и упасть.

                                    В случае записи в базу у вас есть транзакции, в случае чтения файла у вас есть файловый дескриптор. Не понимаю, в общем, ваших стенаний.


                                    1. PsyHaSTe
                                      28.12.2018 11:51

                                      Мы уже разобрали, что с defer доступ в базу закроется. В таком случае данный пример не имеет смысла, го его разруливает достаточно адекватно.

                                      В случае с поломанным конфигом хотеть вы можете много чего, а нужно вам всегда тупо налить логов и упасть.

                                      Еще раз, я апеллирую к тому комменту, где функциия «должна решать на месте». Если функция решает на месте, то я не могу как внешний код отреагировать на ошибку, потому что функция что-то там «порешала», а я об этом ни сном, ни духом. Если же функция каким-то образом сообщает наверх «нишмагла», то это и есть прокидывание ошибки, которое оказывается не go-way.


                                      1. qrKot
                                        28.12.2018 12:43

                                        Если же функция каким-то образом сообщает наверх «нишмагла», то это и есть прокидывание ошибки, которое оказывается не go-way.


                                        Вы слишком прямо восприняли призыв «решать на месте». «Решение на месте», в первую очередь, заключается в выборе «сделать с этим что-то здесь и сейчас» или «прокинуть наверх». Т.е. вся эта вещь, на которую вы упираете, на самом деле лексически-парсится в «программист обязан явно решить, делать ли что-то с ошибкой, проигнорировать возможность ее появления, либо пробросить наверх.»

                                        Никакого запрета на проброс вверх там нет.

                                        if err := someFuncCall(); err != nil {
                                            return err
                                        }
                                        


                                        Вот это совершенно идиоматичный Go-код. Так выглядит «проброс наверх».

                                        Замените `return err` на обработку ошибки, и это будет тоже идиоматичный Go-код (не рекомендованный, к слову, в библиотечных пакетах).

                                        Хотите впрямую заигнорить, `_` вам в помощь.

                                        Единственная фишка языка в плане «должен решать на месте» — это необходимость явно указать, что вы хотите с ошибкой сделать. Нельзя в сигнатуре main написать `throws Exception` и положить на обработку ошибок как таковую.

                                        И вот из-за этого весь сыр-бор.

                                        и есть прокидывание ошибки, которое оказывается не go-way.


                                        Поверьте, совершенно нормальный Go-way, прямо идиоматичный даже.

                                        Единственная реальная трабла обработки ошибок — это многословность. Но это решается, оно уже в бете.


                                        1. PsyHaSTe
                                          28.12.2018 13:41


                                          Тогда я согласен в сами, и не согласен с автором исходного утверждения (а транзитивно — вы тоже с ним не согласны).

                                          В таком случае подождем его ответа, либо обсуждение исчерпано.

                                          Спасибо за диалог.


                          1. qrKot
                            28.12.2018 09:14

                            Если ReadFromFile() паникует, мы не выполняем последнюю строчку и остаемся в неконсистентном состоянии. Если же выполняем, то чему будет равен file.name?


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


              1. Edison
                27.12.2018 19:27

                Зависит от того, насколько критичен конфиг. Eсли не можете загрузить конфиг, можно использовать дефолтный конфиг и WARN в лог или stderr вывести. Если конфиг критичен — то остановить старт.


                1. PsyHaSTe
                  27.12.2018 19:49
                  +1

                  Ну представим, что вы пишете библиотеку, и не знаете, насколько критично отсутствие файла.


                  1. Edison
                    27.12.2018 19:57

                    Библиотека не должна принимать как параметр файл с конфигурацией.

                    Когда я пишу какой-нибудь сервис, который имеет конфигурационный файл, я делаю следующее:
                    1. Создаю дефолтный конфиг.
                    2. Если конфиг файл не был определен — использую дефолтный конфиг.
                    3. Если конфиг файл определен — читаю конфиг файл и переписываю дефолтный конфиг (тот, что был создан сначала).
                    4. Если, когда конфиг файл определен, но что-то идет не так — файла нету, он не читаемый или конфиг не валидный — останавливаю сервис. Тогда упадет только один инстанс, а все другие будут раниться с правильной конфигурацией (пропагация деплоймента остановится).


                    1. PsyHaSTe
                      27.12.2018 20:11

                      Библиотека не должна принимать как параметр файл с конфигурацией.

                      Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее…


                      Если, когда конфиг файл определен, но что-то идет не так — файла нету, он не читаемый или конфиг не валидный — останавливаю сервис. Тогда упадет только один инстанс, а все другие будут раниться с правильной конфигурацией (пропагация деплоймента остановится).

                      Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.


                      1. Edison
                        27.12.2018 20:26

                        Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее

                        Ах интерпрайз.
                        IMO, библиотека должна работать с конфигурационным объектом, а не файлом.

                        Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.

                        Я наверное не понял контекст, ну не падайте раз так, WARNING в лог тогда. Для юзер приложений это может и лучше, для backend — лучше падать.


                        1. PsyHaSTe
                          27.12.2018 21:02
                          +1

                          Ах интерпрайз.
                          IMO, библиотека должна работать с конфигурационным объектом, а не файлом.

                          А объект откуда возмьется?

                          Я наверное не понял контекст, ну не падайте раз так, WARNING в лог тогда. Для юзер приложений это может и лучше, для backend — лучше падать.

                          Ну мое мнение, что нужно прокидывать наверх до тех пор, пока не встретится код, который знает, что делать с ошибкой. А наше обсуждение различных костылей как раз и показывает, что попытка вместо этого порешать всё «на месте» имеет те или иные недостатки.


                          1. Edison
                            27.12.2018 21:34
                            +1

                            А объект откуда возмьется?


                            С конфиг файла, но тут уже вы решите что делать с нечитаемым файлом или его отсутствием, а не полагаться на разработчика библиотеки (вернет ли он ошибку или панику?).
                            Кстати, вот тут ваше мнение расходиться со второй репликой (про прокидование на верх).
                            Второе, чем больше библиотек, тем больше конфиг файлов? А потом еще разный формат — один хочет yaml, второй toml, третий ini.


                            1. PsyHaSTe
                              27.12.2018 21:46

                              С конфиг файла, но тут уже вы решите что делать с нечитаемым файлом или его отсутствием, а не полагаться на разработчика библиотеки (вернет ли он ошибку или панику?).

                              Если библиотека бросает панику — то надо руки такому разработчику вынимать.


                              Кстати, вот тут ваше мнение расходиться со второй репликой (про прокидование на верх).

                              Не расходится. В идеале паник не должно быть вообще нигде, кроме main. Либо делаем что-то, либо прокидываем ошибку выше, всё. Кодекс самурая я линковал.


                              Второе, чем больше библиотек, тем больше конфиг файлов? А потом еще разный формат — один хочет yaml, второй toml, третий ini.

                              Поэтому делайтся генерик — интерфейс (например, в случае выше это Microsoft.Extensions.Configuration), а потом делаются разные реализации (Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Configuration.Xml, Microsoft.Extensions.Configuration.Toml, ...)


                              1. qrKot
                                28.12.2018 09:26

                                Поэтому делайтся генерик — интерфейс (например, в случае выше это Microsoft.Extensions.Configuration), а потом делаются разные реализации (Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Configuration.Xml, Microsoft.Extensions.Configuration.Toml, ...)


                                Выглядит как какая-то жесть и попытка «впихуть невпихуемое».


                      1. powerman
                        27.12.2018 21:52

                        То, что кто-то это делает — не повод считать что это хорошо. Библиотека на то и библиотека, что она не знает, и не должна навязывать конкретный контекст своего выполнения. Всё взаимодействие библиотеки с внешней средой — получение конфига, логирование, доступ к БД — правильнее оформлять через DI, причём принимать все эти зависимости библиотека должна как интерфейсы.


                        Даже если в 99% случаев использования данная библиотека действительно должна тупо читать свой конфиг из файла /etc/библиотека.yml, то она должна предоставить в своём публичном API вспомогательную функцию DefaultConfig(), которая считает этот файл и вернёт значение, подходящее для передачи DI-параметром основному конструктору/инициализатору этой библиотеки. В этом случае эти 99% кода будут содержать что-то вроде:


                        something := some.New(some.DefaultConfig(), mylog, mydb)


                        1. PsyHaSTe
                          27.12.2018 21:54

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


                          1. powerman
                            27.12.2018 22:18

                            Вы не поняли, DefaultConfig возвращает не имя файла с конфигом, а значение содержащее сам конфиг. Откуда это значение получено — из файла, или из другого места — библиотеке дела нет. Использовать DefaultConfig или что-то другое — решает вызывающий код (т.е. main, которые единственный знает текущий контекст выполнения, откуда брать конфиги, куда писать логи, и как подключиться к БД).


                            Если у Вас возникнет логичный вопрос как обработать ошибку самого DefaultConfig, то здесь есть несколько вариантов, выбирать из которых нужно исходя из нужд этой библиотеки.


                            Например, если для библиотеки является штатной ситуацией работа без конфига (точнее, с использованием дефолтного конфига прошитого в самой библиотеке), и отсутствие в системе /etc/библиотека.yml это типичная ситуация, то DefaultConfig() может вообще игнорировать все ошибки и возвращать nil, который библиотека трактует как "взять дефолтный конфиг". Как по мне — это немного грязно, DefaultConfig стоит хотя бы вывести в лог все ошибки кроме отсутствия файла.


                            Но в более типичном/общем случае это делается немного иначе, просто не в одну строку:


                            someConfig, err := some.Defaultconfig()
                            if err != nil {
                                // мы в main(), тут log.Fatal можно
                                log.Fatalf("failed to load config: %s", err)
                            }
                            someThing := some.New(someConfig, log, db)


                            1. PsyHaSTe
                              27.12.2018 23:25

                              Я немног оне о том говорил. DefaultConfig конечно же никаких ошибок не кидает, а возвращает какое-то значение. Но когда мы пытаемся его заменить, получаем ошибку. В вашем примере, если мы в последней строчке падаем, то хочется об этом знать. И не просто в лог написать, а что-то сделать. Ну я, как пользователь библиотеки, хочу написать (на расте, надеюсь, не обидитесь :) ):


                              let config = Config::default();
                              let config = match config.merge(Config::from_file("some/path/config.json")) {
                                 Ok(config) => config,
                                 Err(ParsingError(e)) => config.merge({timeout: 2000}),
                                 Err(e) => panic!("Unexpected error {:?}", e);
                              }

                              То есть если смогли прочитать файл — то ок, если не смогли, но произошла ошибка парсинга, то взять дефолтный конфиг и поменять только параметры таймаута, в противном случае кинуть панику.


                              Если либа за меня решает, что делать ей с ошибкой "на своём уровне", то я никак не могу принять решение там, где это действительно нужно. Если мы прокидываем это в том или ином виде — то это то, чего я хочу, но это противоречит


                              Подход Go подталкивает к обработке ошибок, а не на отсылку их выше


                              1. powerman
                                27.12.2018 23:38

                                В вашем примере, если мы в последней строчке падаем, то хочется об этом знать.

                                Какую строку Вы назвали "последней"? Последняя создаёт someThing, но дальше Вы пишете опять про конфиг, так что я несколько запутался. Если Вы говорили таки про строку с someThing, то там никто не падает, вызов some.New() в этом примере не может завершиться с ошибкой — это довольно типично для простых конструкторов. Если бы там могла возникнуть ошибка (напр. если он как-то проверяет переданный конфиг на валидность), то он просто вернул бы someThing, err.


                                если не смогли, но произошла ошибка парсинга, то взять дефолтный конфиг

                                Это не имеет значения в рамках дискуссии, но брать дефолтный конфиг может иметь смысл при отсутствии конфиг-файла, но ни в коем случае не при ошибке парсинга — если файл был кем-то написан, то это сделано потому, что дефолтный конфиг не подходит, так что в этом случае надо падать с ошибкой.


                                Если либа за меня решает, что делать ей с ошибкой "на своём уровне", то я никак не могу принять решение там, где это действительно нужно.

                                Да, но в моём примере этой проблемы нет — именно по этой причине формирование структуры с конфигом отделено от инициализации библиотеки, чтобы Вы смогли сами обработать ошибку чтения конфига любым образом ещё до инициализации библиотеки.


                                1. PsyHaSTe
                                  28.12.2018 01:29

                                  Да, но в моём примере этой проблемы нет — именно по этой причине формирование структуры с конфигом отделено от инициализации библиотеки, чтобы Вы смогли сами обработать ошибку чтения конфига любым образом ещё до инициализации библиотеки.

                                  Только библиотека знает, валидный ли файл конфига или нет. Как вы это сделаете «еще до инициализации библиотеки»?


                                  1. Edison
                                    28.12.2018 01:44

                                    package main
                                    
                                    import "libA"
                                    
                                    type config struct {
                                        LibA libA.Config
                                        ...
                                    }
                                    
                                    func main() {
                                       var cfg config
                                       if err := c.loadConfig(*configFile); err != nil {
                                           log.Fatal(err)
                                        }
                                        ...
                                    } 

                                    А loadConfig уже парсит конфиг файл и валидирует его в соответствии со структурой.


                                  1. powerman
                                    28.12.2018 02:13

                                    Только библиотека знает, валидный ли файл конфига или нет. Как вы это сделаете «еще до инициализации библиотеки»?

                                    Вы мой пример кода понять пытались вообще? Похоже, что нет. Ладно, попробую объяснить на пальцах, без кода:


                                    1. Дано: есть некая библиотека, достаточно сложная, чтобы ей мог понадобиться собственный конфиг-файл. Тем не менее, хочется избежать создания жёсткой связи между библиотекой, которая может оказаться частью любого приложения выполняющегося в любых условиях, и конкретным конфиг-файлом на диске.
                                    2. Чтобы разорвать эту связь, мы модифицируем функцию-конструктор/инициализатор библиотеки так, чтобы она принимала параметром структуру, описывающую её конфигурацию, вместо того, чтобы самостоятельно считывать и парсить конкретный конфиг-файл. Таким образом, ответственность за считывание и парсинг конфиг-файла переносится на вызывающий код, обычно это main() конкретного приложения, знающего условия в котором оно выполняется, и решающего, откуда и как получить конфигурацию для этой библиотеки.
                                    3. Тем не менее, предполагая, что большинству приложений подойдёт подход по умолчанию, заключающийся в считывании конфигурации из конкретного файла, мы добавляем в библиотеку отдельную, опциональную, независимую вспомогательную функцию, которая умеет исключительно считать этот конфиг-файл и вернуть структуру с конфигурацией, необходимую для инициализации библиотеки.
                                    4. В результате, main() имеет возможность проинициализировать библиотеку в два шага: сначала вызвав вспомогательную функцию для считывания конфиг-файла, и потом передав структуру с конфигурацией в функцию инициализирующую библиотеку. На обоих шагах могут возникнуть ошибки, которые main() может обработать по своему разумению.

                                    Помимо прочего, такой подход крайне рекомендуется для упрощения тестирования библиотеки, т.к. тест сможет легко подсовывать библиотеке разные структуры с конфигурацией, без необходимости создавать/изменять конфиг-файл(ы) на диске. Это и есть типичный пример тех "разных условий, в которых может выполняться библиотека", в которых безусловное чтение конфиг-файла может оказаться неуместным.


                                    1. PsyHaSTe
                                      28.12.2018 11:53

                                      Дано: есть некая библиотека, достаточно сложная, чтобы ей мог понадобиться собственный конфиг-файл.

                                      Нет, конечно же. Библиотека с конфиг-файлом это антипаттерн, библиотека должна конфигурироватсья через код.

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

                                      Соответственно, всё остальное уже следствие из неверной посылки.


                                      1. qrKot
                                        28.12.2018 12:43

                                        К конфигам чего, простите, предоставляет доступ сторонняя библиотека?


                              1. Edison
                                27.12.2018 23:47

                                Ну вы же вот прям у себя в коде с match обработали ошибку.
                                Я могу точно такой же код на Go написать.


                                1. PsyHaSTe
                                  28.12.2018 01:31

                                  Ну так, смысл в том, что у нас есть наш app, и зависимый пакет config. Так вот, ошибка происходит в последнем, и у него есть два способа:


                                  1. Пробросить ошибку в app, который соответственно по ней сделает матч и примет какое-то решение
                                  2. Решит проблему "на месте", и сделает что-то друоге: вернет конфиг по-умолчанию, вернет nil, ко всему этому напишет в лог, запаникует и т.п.

                                  Я всегда считал, что правильный вариант — 1, но как выше говорилось, "go way" в том, чтобы следовать варианту 2 в той или иной форме.


                                  1. Edison
                                    28.12.2018 01:40

                                    А зачем вам отдельный пакет config? Очень часто это просто ненужно.
                                    Опять же, как я делаю:
                                    1. В текущем пакет есть структура `Config`.
                                    2. Структура `Config` содержит конфигурацию все других пакетов.
                                    3. Все обработка конфиг файла в пакете `main`.

                                    Если брать ваш случай, то библиотека не должна работать с конфиг файлом (только структуры и их валидация, а не как вы примеры делали с .NET). Если уже есть такое, то при любой ситуации вернуть ошибку. Библиотеки никогда не должны принимать решения, только делегация. Вот и все.


                                    1. PsyHaSTe
                                      28.12.2018 01:45

                                      А зачем вам отдельный пакет config? Очень часто это просто ненужно.

                                      Затем, чтобы не писать тыщи строк загрузки/валидации/парсинга/опциональных значчений/автоподгрузки изменений с разных файловых систем/черта в ступе, а иметь всё из коробки…

                                      Если брать ваш случай, то библиотека не должна работать с конфиг файлом (только структуры и их валидация, а не как вы примеры делали с .NET).

                                      Ну как не должна. Как вы, например, сделаете, что у вас есть сервис (MVC контроллер, к примеру), который берет параметр из конфига, и при изменении конфига в ФС через DI автоматически прокинуть имзенения в контроллер, чтобы последующие вызовы его методов использовали уже новый конфиг?


                                      1. powerman
                                        28.12.2018 04:04

                                        Если Вам нужен очень умный самообновляющийся конфиг, то всё делается ровно так же, как я описывал — просто вместо тупой структуры с конфигурацией мы передаём в функцию инициализации библиотеки объект с методами, соответствующими интерфейсу Config. Библиотека получает значения конфига через вызов методов этого объекта в тот момент, когда они ей нужны, а откуда этот объект их берёт и каким образом обновляет на лету — библиотеке не важно. Суть остаётся ровно та же самая: мы можем передать библиотеке объект конкретного типа возвращённый функцией DefaultConfig (работающий через чтение/парсинг/перечитывание дефолтного файла с конфигурацией), а можем передать объект любого другого типа с теми же методами (напр. тестовый мок, который содержит прошитые в тесте значения конфигурации и изменяет их в момент, когда это нужно тесту).


                                        1. PsyHaSTe
                                          28.12.2018 11:54

                                          А кто будет писать все эти «самообновления»? В моем случае, за меня уже написал майкрософт, надо просто подключить библиотеку, и дать ей файлик. А на выходе я получу распаршеное значение, которое умеет во все эти самообновления.


                                          1. qrKot
                                            28.12.2018 12:45

                                            Кхм, поищите написанный «самообновления» под Go. Если это действительно часто бывает нужно (на самом деле, нет, конечно), то наверняка есть пара проектов на github'е, которые это умеют.


                                      1. qrKot
                                        28.12.2018 09:42

                                        Затем, чтобы не писать тыщи строк загрузки/валидации/парсинга/опциональных значчений/автоподгрузки изменений с разных файловых систем/черта в ступе, а иметь всё из коробки…


                                        В том и прикол, что библиотека не должна содержать в себе код работы с «парсингом разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе» (за исключением тех случаев, конечно, когда это библиотека парсинга разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе).

                                        Библиотека должна получать конфиг при инстанцировании. Готовый. Не файл на диске, а готовых прочитанный/сконструированный/собранный вручную/пофиг, откуда он вообще взялся, но однозначно готовый к использованию конфиг.

                                        Ну как не должна. Как вы, например, сделаете, что у вас есть сервис (MVC контроллер, к примеру), который берет параметр из конфига, и при изменении конфига в ФС через DI автоматически прокинуть имзенения в контроллер, чтобы последующие вызовы его методов использовали уже новый конфиг?


                                        Ну, во-первых, вы сейчас какую-то жесть описали.

                                        Во вторых, если уж вам динамическое обновление конфига нужно, видимо, вам нужно просто при изменении конфига перечитывать его (это же логично?) и скармливать библиотеке новый конфиг. Не файл, а готовый к употреблению конфиг. Иначе вы запустите два приложения, использующих внутри одну библиотеку, которые будут бесконечно сраться за доступ к общему файлу конфигурации.


                                        1. PsyHaSTe
                                          28.12.2018 11:56
                                          -1

                                          В том и прикол, что библиотека не должна содержать в себе код работы с «парсингом разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе» (за исключением тех случаев, конечно, когда это библиотека парсинга разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе).

                                          Ну так это она и есть :) Это достаточно базовй функционал, для библиотеки работы с конфигами.


                                          Конфиг нужен моему приложению. Но я не собираюсь руками json'ы парсить, писать какой-то код по подгрузке изменений и всё это. Я отдам файл библиотеке, которая вернет мне объект, который всё это умеет.


                                          Во вторых, если уж вам динамическое обновление конфига нужно, видимо, вам нужно просто при изменении конфига перечитывать его (это же логично?) и скармливать библиотеке новый конфиг.

                                          Вы неправильно поняли. Не библиотеке нужен конфиг, а приложению. Библиотека является тем, что парсит конфиг, и умеет во все умные штуки.


                                          1. qrKot
                                            28.12.2018 13:03

                                            Это достаточно базовй функционал, для библиотеки работы с конфигами.


                                            Вот я сейчас одного не понимаю, зачем вам библиотека для работы с конфигами. Применения я вижу, собственно, 2:
                                            1. У вас 100500 приложений, работающих с одной и той же конфигурацией (отличная иллюстрация к книге «Первый опыт в программировании: как делать не надо»)
                                            2. Я боюсь даже представить, в каком виде хранится ваш конфиг, что для того, чтобы его распарсить, нужно что-то уровня SOAP2 (это в другую книжку: «Лучшие практики энтерпрайза: 10 самых оверинжениренных хреней в истории человечества»).

                                            Но я не собираюсь руками json'ы парсить


                                            configData, err := ioutil.ReadFile("/path/to/config")
                                            if err != nil {
                                                log.Fatal(err)
                                            }
                                            var config map[string]interface{}
                                            config, err := json.Unmarshall(configData, &config)
                                            if err != nil {
                                                log.Fatal(err)
                                            }
                                            


                                            Вот это вы собираетесь в отдельную библиотеку выносить? Простите, leftpad'ом запахло.

                                            Или вы туда еще 5 строчек работы с fs.notify принесете? Ну, как бы, да, так существование библиотеки будет более осмысленным (на самом деле нет).

                                            Или вы хотите обработку стейта вашего приложения в зависимости от нового конфига в рантайме в библиотеку унести? А нафига, собственно, если эта библиотека будет нужна ровно одному приложению (если, конечно, у вас не 100500 совершенно одинаковых приложений)?

                                            Я отдам файл библиотеке, которая вернет мне объект, который всё это умеет.


                                            Реализация всего этого удовольствия займет у вас строк 50… Реально в библиотеку понесем? Отраслевым стандартом объявим, поместим его рядом с остальными 14-ю?

                                            Не библиотеке нужен конфиг, а приложению. Библиотека является тем, что парсит конфиг, и умеет во все умные штуки.


                                            Понимаете, приложениям бывает нужен конфиг, тут да. Но трабла в том, что каждому приложению, как правило, нужен свой конфиг (на то они и разные приложения). А писать отдельную библиотеку, которая будет отдавать конфиг для одного приложения — а нахрена она за пределами этого приложения нужна?


                                            1. Edison
                                              28.12.2018 13:14

                                              Есть такая гошная библиотека — github.com/spf13/viper

                                              Так вот там все — парсинг всех форматов, перезагрузка конфига при изменении и тд.
                                              Я ее как-то выпилил из одного сервиса — нам нужен был просто парсер yaml и все, так размер бинарника уменьшился с 10 MB до 4MB.


                                              1. PsyHaSTe
                                                28.12.2018 13:46

                                                Выглядит как отличная библиотека. Я бы заиспользовал. О чем-то таком я и говорил, да.

                                                Я ее как-то выпилил из одного сервиса — нам нужен был просто парсер yaml и все, так размер бинарника уменьшился с 10 MB до 4MB.

                                                Ну тут вариант два: либо вы изначально взяли не тот инструмент и заменили его на правильный, и это хорошо, либо вы реализовали свой ограниченный и скорее всего нестабильный вариант подмножества этой библиотеки, и тогда это уже не очень. В последнем случае должен спасать LTO.


                                                1. Edison
                                                  28.12.2018 15:05
                                                  +1

                                                  Выглядит как отличная библиотека. Я бы заиспользовал. О чем-то таком я и говорил, да

                                                  У нас с вами явно разные предпочтения.

                                                  Ну тут вариант два: либо вы изначально взяли не тот инструмент и заменили его на правильный, и это хорошо, либо вы реализовали свой ограниченный и скорее всего нестабильный вариант подмножества этой библиотеки, и тогда это уже не очень. В последнем случае должен спасать LTO.

                                                  Это был сервис, который достался мне в наследство.
                                                  `viper` использует `gopkg.in/yaml.v2` для парсинга yaml файлов. Я просто взял этот парсер и сам распарсил конфиг файл в структуру (вот как выше qrKot показал, 4 строки). Что тут может быть нестабильного? Потому что мне не нужно > 20 транзитивных зависимостей.


                                                  1. PsyHaSTe
                                                    28.12.2018 15:52

                                                    У нас с вами явно разные предпочтения.

                                                    DRY во все поля. Стараюсь соблюдать. Если есть библиотека для Х, не надо писать Х руками. Даже если это падлефт.


                                                    1. Edison
                                                      28.12.2018 17:23

                                                      Тянуть 51 зависимость ради 5 строк кода — замечательно.


                                                    1. powerman
                                                      28.12.2018 19:06

                                                      Если есть библиотека для Х, не надо писать Х руками. Даже если это падлефт.

                                                      А вот это действительно антипаттерн в Go.


                                                      Есть языки, в которых этот X написать самостоятельно — только нарываться на лишние проблемы. Потому что очень легко сделать не очевидные ошибки, потом придётся их долго фиксить, и, через годик фиксов, мы получим небольшого монстрика, с местами не очевидным кодом, но зато корректно работающего… и, учитывая, что ровно такого же монстрика мы могли получить ещё год назад просто взяв стороннюю библиотеку — выбор очевиден.


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


                                                      1. PsyHaSTe
                                                        29.12.2018 01:56

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

                                                        Свои строки, свои конфиги, своё всё… Где-то такой паттерн я уже встречал…

                                                        Каким таким образом го убирает все ошибки тоже непонятно. Больше похоже на то, что автор забьет на обработку граничных случаев (что сократит код раз в 10), а программисты на проекте которые нарушат представления автора о том, как все это должно работать окажутся сами виноваты, потому что «не надо это так использовать».

                                                        Есть языки, в которых этот X написать самостоятельно — только нарываться на лишние проблемы.

                                                        Любой home made код хуже, чем проестированный, проверенный временем специализированный код библиотеки. Мне как пользователю пофиг, сколько там зависимостей, все равно LTO вырежет все, что не используется. А вот надежность, и сэкономленное время сильно важнее.

                                                        Конечно, если у вас была зависимость на крейт с 3 звездами на гитхабе, то это другой вопрос. Но библиотека на которую была ссылка выглядит вполне здоровой.


                                                        1. powerman
                                                          29.12.2018 02:58

                                                          Свои строки, свои конфиги, своё всё… Где-то такой паттерн я уже встречал…

                                                          Мимо, никто в Go не делает свои строки, очень много есть в стандартной библиотеке, свой парсер yaml/ini тоже никто в своём уме писать не станет. Но и ради leftpad тоже никто стороннюю библиотеку подключать не будет.


                                                          Любой home made код хуже, чем проестированный, проверенный временем специализированный код библиотеки.

                                                          Это не так. Опенсорсные библиотеки (а мы ведь о них говорим, да?) в основной массе и есть типичный home made код. Причём, как правило, более универсальный, функциональный и сложный, чем нужно для текущего проекта, что зачастую с лихвой перекрывает "протестированность и проверенность временем" и приводит к тому, что работает это менее надёжно, чем своя минималистичная реализация.


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


                                                          Например, если в нашей команде нет сильных разработчиков или они все перегружены более важной работой — да, смысл взять стороннее решение заметно увеличивается, даже если задача относительно несложная. Но в случае сильных разработчиков и конкретно Go нередко получается так, что написать что-то несложное самостоятельно тупо дешевле по суммарной стоимости (включая стоимость дальнейшей поддержки), чем искать, изучать, подключать, допиливать, делать ревью кода, общаться с автором в PR пытаясь протолкнуть свои фиксы/фичи, форкать и делать повторные ревью при обновлениях (либо забить на ревью, брать чужой код не глядя, и иметь дело с последствиями, у чего тоже своя цена) сторонней библиотеки.


                                                          В качестве конкретного примера, в моём текущем проекте (50k строк) подключены 22 библиотеки, из них ровно половина это либо наши собственные опенсорсные, либо дополнения стандартной библиотеки Go (из golang.org/x/…). Из оставшихся 11 часть это (относительно) официальные библиотеки (драйвер DB, SDK сервисов вроде AWS, Stripe, etc.), без них остаётся 6, из которых только одна достаточно тривиальная, без которой можно было бы обойтись. При этом в этих 50к строк есть ровно один настоящий велосипед на 1600 строк (по ряду причин пришлось сделать свою очередь сообщений, и перед тем как решиться писать велосипед я честно неделю старался заюзать существующий NATS Streaming Server, и до сих пор не рад, что пришлось таки написать этот велосипед).


                                                          Условно говоря, если проблему можно решить в 20 строк кода, то никто ради этого библиотеку подключать даже не подумает — библиотека тупо обойдётся дороже. А если Вы не в состоянии написать эти 20 строк без багов, и этим оправдываете необходимость подключения библиотеки…


                                                          1. PsyHaSTe
                                                            29.12.2018 05:05

                                                            Это не так. Опенсорсные библиотеки (а мы ведь о них говорим, да?) в основной массе и есть типичный home made код. Причём, как правило, более универсальный, функциональный и сложный, чем нужно для текущего проекта, что зачастую с лихвой перекрывает «протестированность и проверенность временем» и приводит к тому, что работает это менее надёжно, чем своя минималистичная реализация.

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

                                                            Не поймите меня неправильно, я не предлагаю писать свои велосипеды где надо и где не надо, речь немного о другом: добавление в проект новой зависимости это не бесплатно, у сторонних библиотек есть своя цена.

                                                            Есть, но она копеечная. Если про тот же дотнет мы говорим, то на время компиляции зависимости вообще не влияют, а добавление того же Configuration добавляет чуть больше 100КБ на диске.

                                                            image

                                                            Действительно ли это такая страшная цена за качественное, проверенное решение?


                                                            1. qrKot
                                                              29.12.2018 05:37

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


                                                              Кхм, вы абсолютно зря, например, в этом уверены. Кусок кода, отвечавший за нормализацию строки в .NET Core сами нагуглите, или нагуглить за вас?

                                                              Действительно ли это такая страшная цена за качественное, проверенное решение?


                                                              Вы не то в цену добавляете. Цена — это не место на диске, и не время компиляции. Цена — это зависимость вашего кода от движения левой пятки стороннего разработчика. В случае импортов из стандартной библиотеки или из x, особых проблем нет — это поддерживается. В случае leftpad'ов — это всегда плохая идея.


                                                            1. powerman
                                                              29.12.2018 06:04

                                                              Цена библиотек вовсе не в килобайтах и времени компиляции. Если разрабатывается прототип — цена библиотек нулевая. Но в нормальных проектах каждая зависимость создаёт дополнительные риски и увеличивает нагрузку на поддержку проекта.


                                                              Библиотеки надо отслеживать (как минимум на предмет выхода новых версий, исправляющих проблемы с безопасностью), их надо обновлять (что нередко требует внесения изменений в свой код), в проектах где важна безопасность код библиотек нужно хотя бы проглядывать перед добавлением плюс смотреть изменения при обновлениях, если возникают проблемы (а они всегда возникают, даже с отлаженными за 10 лет библиотеками) то их решение обходится намного дороже чем исправление бага или добавление фичи в своём коде (нужно потратить время на коммуникацию с автором библиотеки, если он оперативно фикс не выпустит, то нужно самим вникнуть в код библиотеки — которая довольно большая и содержит много ненужного текущему проекту — и пофиксить самостоятельно, после чего либо ждать пока автор примет PR, либо делать свой форк, в случае форка появляется дополнительная нагрузка на подтягивание в него обновлений из основного репо, etc.).


                                                              Помимо этого использование избыточно функциональных библиотек нередко приводит к трате лишних ресурсов на выполнение ненужного этому проекту кода, что отрицательно сказывается на скорости его работы и требованиям к ресурсам (RAM/CPU). Ещё одна проблема связана с тем, что быстро подключив навороченную библиотеку мы нередко добавляем в проект "бесплатные" фичи — то, что проекту не нужно, но что мы получили вместе с библиотекой (возвращаясь к примеру с конфиг-файлом такой фичей является обновление конфигурации при изменении файла на лету), что тоже удорожает поддержку (не нужные, но реализованные фичи приходится поддерживать) и в целом ведёт проект в сторону bloatware.


                                                              Цена библиотек достаточно высока. Она однозначно является приемлемой если библиотека решает достаточно сложную задачу, но для задач, которые можно самостоятельно решить в 20 строк — использование сторонних библиотек это просто вредительство.


                                                              1. PsyHaSTe
                                                                29.12.2018 12:08

                                                                Библиотеки надо отслеживать (как минимум на предмет выхода новых версий, исправляющих проблемы с безопасностью)

                                                                Не помню ни одного исправления ни одной библиотеки в дотенете, которая исправляла бы безопасность. CVE самого дотнета выходят как патчи на винду, а вот библиотеки… Ни разу.

                                                                их надо обновлять (что нередко требует внесения изменений в свой код)

                                                                На куче проектов, что я видел, использовались версии библиотек на 2-3 мажорных версии меньше последнего стейбла. Потому что обычно разнца только в добавлении новых фич, которые не нужны особо, т.к. затраты на обновления кода и риски развала проекта перевешивают. Собственно, любое обновление библиотек всегда рассматривается с точки зрения стоимости переписывания/вероятности развала проекта в уже работающих сценариях. Безопасность библиотек вообще никак не рассматривается, ибо в них и уязвимостей-то нет. Если не верите, самый популярный пакет — Json.Net, попробуйте в нем найти релиз, который исправляет какую-то проблему с безопасностью. Если найдете — то наверное нужно признать, что сообщество просто более безответственное.

                                                                в проектах где важна безопасность код библиотек нужно хотя бы проглядывать перед добавлением плюс смотреть изменения при обновлениях, если возникают проблемы

                                                                Безопасность важна везде, никто не хочет, чтобы данные клиентов утекли, но я не припомню, чтобы хоть кто-то лопатил мегабайты изменений чтобы что-то найти. В таком случае проще опять же не обновляться и жить годами на одной версии.

                                                                если возникают проблемы (а они всегда возникают, даже с отлаженными за 10 лет библиотеками)

                                                                Да нет, с большими библиотеками проблем не возникает. Упомянутый Json.Net решает все сценарии. Бывает, не хватает фич, но фича != баг, и система расширения библиотеки обычно предусматривает возможность приткнуть свои классы для добавления этой функциональности.

                                                                то их решение обходится намного дороже чем исправление бага или добавление фичи в своём коде (нужно потратить время на коммуникацию с автором библиотеки, если он оперативно фикс не выпустит, то нужно самим вникнуть в код библиотеки — которая довольно большая и содержит много ненужного текущему проекту — и пофиксить самостоятельно, после чего либо ждать пока автор примет PR, либо делать свой форк, в случае форка появляется дополнительная нагрузка на подтягивание в него обновлений из основного репо, etc.).

                                                                А вот с этим соглашус на 100% — пулл реквесты в библиотеки это действительно на порядок сложнее, чем дописать свой код. Единственное, с чем я не могу никак поспорить, и абсолютно с вами солидарен.

                                                                Помимо этого использование избыточно функциональных библиотек нередко приводит к трате лишних ресурсов на выполнение ненужного этому проекту кода, что отрицательно сказывается на скорости его работы и требованиям к ресурсам (RAM/CPU).

                                                                Ну это уже странные штуки пошли. Библиотека обычно ничего такого страшного не делает, если это не ORM с километровыми запросами. Хотя там, где важны каждые 1-2% производительности дотнет обычно не используют, мб в Го разница между 98 и 100 мс на ответ является клиентообразующей.

                                                                Ещё одна проблема связана с тем, что быстро подключив навороченную библиотеку мы нередко добавляем в проект «бесплатные» фичи — то, что проекту не нужно, но что мы получили вместе с библиотекой (возвращаясь к примеру с конфиг-файлом такой фичей является обновление конфигурации при изменении файла на лету), что тоже удорожает поддержку (не нужные, но реализованные фичи приходится поддерживать) и в целом ведёт проект в сторону bloatware.

                                                                Это наоборот плюс же. Неиспользуемые фичи могут хоть удалится из библиотеки, и ваш код ничего не узнает, вы же не использовали их. Если же использовал, то фичи не бесполезные, и сэкономили вам время.


                                                                1. qrKot
                                                                  29.12.2018 12:56

                                                                  Не помню ни одного исправления ни одной библиотеки в дотенете, которая исправляла бы безопасность


                                                                  Видимо, не следите.

                                                                  CVE самого дотнета выходят как патчи на винду


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

                                                                  На куче проектов, что я видел, использовались версии библиотек на 2-3 мажорных версии меньше последнего стейбла.


                                                                  И это говорит не в пользу экосистемы, например. Гордиться тут, прямо скажем, нечем.

                                                                  Потому что обычно разнца только в добавлении новых фич, которые не нужны особо, т.к. затраты на обновления кода и риски развала проекта перевешивают.


                                                                  Т.е. принят подход «работает — не трожь». Вероятно, и убежденность в том, что фиксов уязвимостей под .net-библиотеки нет, растет оттуда же? «риски развала проекта» важнее, чем отсутствие уязвимостей в коде… Ну, ок…

                                                                  Если не верите, самый популярный пакет — Json.Net


                                                                  По какому, собственно, критерию, самый популярный? Самый популярный лично у вас?

                                                                  Не поленился, сходил на NuGet (я же в правильном месте ищу?). В выдаче первые 3: NUnit, Newtonsoft.JSON, EntityFramework…

                                                                  А вот тут про уязвимость

                                                                  И тут.

                                                                  Цитируем вас: «нужно признать, что сообщество просто более безответственное. „

                                                                  В таком случае проще опять же не обновляться и жить годами на одной версии.


                                                                  Отличное решение, такЪ победимЪ.

                                                                  Да нет, с большими библиотеками проблем не возникает.


                                                                  Кхм, либо они недостаточно большие, либо вы не замечаете. Большие библиотеки на то и большие, что в них без проблем не может обходиться.

                                                                  Хотя там, где важны каждые 1-2% производительности дотнет обычно не используют, мб в Го разница между 98 и 100 мс на ответ является клиентообразующей.


                                                                  Ну, допустим, не 1-2% “зазора производительности» для дотнета нормальными считаются, а, скорее, 15-25%. Там, где важны 1-2%, юзают C. А Go ценят больше за утилизацию ресурсов.

                                                                  Это наоборот плюс же.


                                                                  В чем, простите, плюс?

                                                                  Неиспользуемые фичи могут хоть удалится из библиотеки, и ваш код ничего не узнает, вы же не использовали их.


                                                                  С тем же успехом используемые фичи могут начать вести себя иначе, и ваш код ничего не узнает.


                                                                  1. PsyHaSTe
                                                                    29.12.2018 13:13

                                                                    Не поленился, сходил на NuGet (я же в правильном месте ищу?). В выдаче первые 3: NUnit, Newtonsoft.JSON, EntityFramework…

                                                                    А вот тут про уязвимость

                                                                    И тут.

                                                                    Цитируем вас: «нужно признать, что сообщество просто более безответственное. „

                                                                    Благодарю, любопытная информация.

                                                                    Отличное решение, такЪ победимЪ.

                                                                    Да нет, не очень хорошо, но так вот есть.

                                                                    В чем, простите, плюс?

                                                                    В том, что при необходимости ими можно вспользоваться.

                                                                    С тем же успехом используемые фичи могут начать вести себя иначе, и ваш код ничего не узнает.

                                                                    Ну и пусть, мы же ими все равно не пользуемся, и поведения у них для нас никакого нет.


                                                                    1. qrKot
                                                                      29.12.2018 13:25

                                                                      Ну и пусть, мы же ими все равно не пользуемся, и поведения у них для нас никакого нет.


                                                                      Они «занимают место» — это, зачастую, малозначимо, но оно таки минус.

                                                                      «Плюс» того, что «этим можно воспользоваться»… Кхм, без пересборки вашего приложения — нет, нельзя. Т.е. не плюс.


                                                            1. Edison
                                                              29.12.2018 11:23

                                                              тут недавно был случай внедрения вредоносного кода в зависимость очень популярной js библиотеки.


                                            1. 0xd34df00d
                                              28.12.2018 22:52
                                              +1

                                              Вот это вы собираетесь в отдельную библиотеку выносить?

                                              А где валидация? Где обработка отсутствующих случаев? Где переопределение аргументов с командной строки? Где генерация хелпа?


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

                                              Поэтому есть вещи вроде таких.


                                              1. qrKot
                                                29.12.2018 09:58

                                                А где валидация?


                                                В типах.

                                                Где обработка отсутствующих случаев?


                                                В инициализации.

                                                Где переопределение аргументов с командной строки?


                                                Нигде. Аргументы командной строки ортогональны конфиг-файлу.

                                                Где генерация хелпа?


                                                В сторонних генераторах хелпов?

                                                Поэтому есть вещи вроде таких.


                                                Каким боком, простите, парсилка аргументов командной строки относится к хот-релоаду конфиг-файла?


                                  1. qrKot
                                    28.12.2018 09:36

                                    Ну так, смысл в том, что у нас есть наш app, и зависимый пакет config.


                                    У вас граф зависимостей поломался. config от app не зависит, это app зависит от config же.

                                    но как выше говорилось, «go way» в том, чтобы следовать варианту 2 в той или иной форме.


                                    Еще у вас когнитивный аппарат поломался, видимо. Вам выше с примерами кода показали, что как раз Go-way — отдать ошибку наверх, чтобы вызывающая сторона могла решить, что с ней делать.


                                    1. PsyHaSTe
                                      28.12.2018 11:57

                                      У вас граф зависимостей поломался. config от app не зависит, это app зависит от config же.

                                      Криво написал. конечно же app зависит от config.


                                      Еще у вас когнитивный аппарат поломался, видимо. Вам выше с примерами кода показали, что как раз Go-way — отдать ошибку наверх, чтобы вызывающая сторона могла решить, что с ней делать.

                                      Это прямо противоречит


                                      Подход Go подталкивает к обработке ошибок, а не на отсылку их выше


                                      1. qrKot
                                        28.12.2018 13:04

                                        Он подталкивает. И «взять ошибку и прокинуть выше» — это тоже обработка. Он просто заставляет сделать с ошибкой хоть что-нибудь.


                              1. qrKot
                                28.12.2018 09:32

                                Вот прямо ваш же код возьмем и перепишем, если я правильно распарсил ваши намерения:

                                config := conf.Deafult()
                                
                                if err := config.FromFile("some/path/config.json"); err != nil {
                                   // ... вот тут паникуем, страдаем, фиксим, обрабатываем
                                   // и пофиг чем вообще занимаемся...
                                }
                                
                                service, err := serv.NewAwesomeService(&config)
                                
                                // ... дальше остальная муть
                                


                      1. qrKot
                        28.12.2018 09:25

                        Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее…


                        Очевидно, вы считаете это лучшими примерами того, как нужно делать?

                        Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.


                        Поломанный конфиг — это не любой чих. Это заведомо некорректная рантайм-конфигурация. Это сервис, который, например, пишет не в ту базу, или возвращает отладочные результаты на запрос… И вы предлагаете его пустить в прод?


                        1. PsyHaSTe
                          28.12.2018 11:59

                          Очевидно, вы считаете это лучшими примерами того, как нужно делать?

                          Конечно. Лучше иметь пакет для инкапсуляции какого-то поведения, чем не иметь.


                          Поломанный конфиг — это не любой чих. Это заведомо некорректная рантайм-конфигурация. Это сервис, который, например, пишет не в ту базу, или возвращает отладочные результаты на запрос… И вы предлагаете его пустить в прод?

                          Конечно, любой чих. Например, Если это конфиг какого-нибудь мода на скайрим, ронять всю игру из-за того что мы не смогли распарсить текстуру какого-нибудь модного меча из файла это не очень хорошо.


                          1. qrKot
                            28.12.2018 13:13

                            Если это конфиг какого-нибудь мода на скайрим, ронять всю игру из-за того что мы не смогли распарсить текстуру какого-нибудь модного меча из файла это не очень хорошо.


                            Если мы не смогли распарсить текстуру какого-нибудь модного меча в Скайриме, это повод не подгрузить меч и, возможно, продолжить работу дальше (остается на совести разработчика). Но текстура меча — это вполне себе отдельная сущность, которую можно считать частью конфигурации меча, не Скайрима. Если Скайрим не сможет прочитать свою конфигурацию (да-да, целиком), он ровно так же упадет, и это правильно.

                            В контексте же сферы применения Go (я надеюсь, у вас нет мысли переписать на нем Скайрим? Если есть — немедленно бросьте, гуано получится. Точнее, ничего вообще не получится). В серверных приложениях (где Go цветет и пахнет) нет подхода «половины конфига нету, ну и срать, работаем хотя бы как-то». Либо работаем гарантированно корректно, либо не работаем вообще. Т.к. конечный пользователь, который вам деньги платит, может с некоторым пониманием отнестить с даунтайму всего сервиса на некоторое время, при условии, что он на этом ничего кроме времени не потерял. А вот к тому, что, допустим, его фотки с обнаженкой из-за левых настроек гейта ушли вместо его визави по эротической переписке в голландский филиал «Голубой устрицы» и попали в ее публичный альбом «ведущие гомосексуалисты месяца» — вот тут пользователь может сильно не понять.

                            Ну а если поломанный конфиг не влияет на работу сервиса от слова «совсем»… Хм, а нафига вам вообще конфиг?


                            1. PsyHaSTe
                              28.12.2018 13:49

                              Если мы не смогли распарсить текстуру какого-нибудь модного меча в Скайриме, это повод не подгрузить меч и, возможно, продолжить работу дальше (остается на совести разработчика). Но текстура меча — это вполне себе отдельная сущность, которую можно считать частью конфигурации меча, не Скайрима. Если Скайрим не сможет прочитать свою конфигурацию (да-да, целиком), он ровно так же упадет, и это правильно.

                              Да, но и для чтения конфига мода, и для чтения конфига меча используется одна и та же либа :) Но в одном случае надо падать, а в другом — «забить» на ошибочный мод и работать дальше. Соответственно, это был пример того, что на уровне библиотеки нельзя принять правильное решение, она не знает, насколько это критичный конфиг. Решение одно — прокинуть ошибку наверх, пусть там разбираются. Собственно, мы в этом уже сошлись, поэтому не вижу смысла на этом задерживаться.

                              В контексте же сферы применения Go

                              Я думал, цели го более широки, чем только лишь веб-серверы.


                              1. qrKot
                                29.12.2018 10:04

                                Да, но и для чтения конфига мода, и для чтения конфига меча используется одна и та же либа :)


                                Хм, вы твердо уверены, что текстовый конфиг игры и bitmap-текстуры игровой модели (которые вы приводили как пример выше) должны парситься одной и той же библиотекой? У нас, видимо, разные с вами понятия о здравом смысле.

                                Но в одном случае надо падать, а в другом — «забить» на ошибочный мод и работать дальше.


                                Фоллбек-логику, в смысле, вы тоже предлагаете в стороннюю библиотеку отдать? И пусть игрок вместо меча с розовым фаллоимитатором в руках бегает. А чо, прикольно же.

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


                                Воот, библиотека может либо отдать целиком корректный конфиг, либо ругнуться наверх «конфиг поломатый» и не отдать ничего. Половинчатый или неконсистентный объект конфигурации — всегда плохая идея.

                                Я думал, цели го более широки, чем только лишь веб-серверы.


                                Чуть-чуть более, но не настолько, насколько вы представляли. Go — достаточно нишевой и строго утилитарный язык.


                                1. PsyHaSTe
                                  29.12.2018 12:12

                                  Хм, вы твердо уверены, что текстовый конфиг игры и bitmap-текстуры игровой модели (которые вы приводили как пример выше) должны парситься одной и той же библиотекой? У нас, видимо, разные с вами понятия о здравом смысле.

                                  Ну возьмите вместо текстуры просто JSON с описанием характеристик меча. Текст-описание-урон, вот это всё.

                                  Фоллбек-логику, в смысле, вы тоже предлагаете в стороннюю библиотеку отдать? И пусть игрок вместо меча с розовым фаллоимитатором в руках бегает. А чо, прикольно же.

                                  В приложение. И не с розовым фаллоимитатором, а с черным квадратиком, tooltip missing, дефолтным мечом или чем-либо еще.
                                  image

                                  Воот, библиотека может либо отдать целиком корректный конфиг, либо ругнуться наверх «конфиг поломатый» и не отдать ничего. Половинчатый или неконсистентный объект конфигурации — всегда плохая идея.

                                  Вообще ни разу не предлагал возвращать кусок конфига :D


                                  1. qrKot
                                    29.12.2018 13:08

                                    Вообще ни разу не предлагал возвращать кусок конфига :D


                                    А что вы предлагали?

                                    Вы утверждали, что для парсинга JSON-конфига вам нужна сторонняя мегабиблиотека, которая должна производить валидацию, выводить информативные сообщения об ошибке в парсинге, предполагать какую-то логику по хот-релоаду и вот это вот все.

                                    Итак, остаются нерешенными 2 вопроса:
                                    1. Зачем вам логика парсинга JSON в конструкторе этого самого «меча»?
                                    2. Чем случай «json-файл с конфигом отсутствует» в вашем конкретном случае отличается от «json-файл с конфигом не читается» или «json-файл с конфигом содержит некорректные данные»?

                                    Вот смотрите, у вас же, видимо, на весь «МойСкайрим_v1.0Beta» несколько больше одного конфига? У вас же, надеюсь, конфиг самой игры, содержащий разрешение экрана, режим работы, настройки управления и т.д. и конфигурация ресурсов игры — это разные файлы? Причем, вероятно, на каждый «убер-меч» конфиг свой? Или вы враг себе и игроку?

                                    Ну вот объясните мне, что, собственно, должна ваша библиотека по загрузке конфигурации делать такого, чего не может стандартная json-парсилка, которая возвращает инстанс объекта + nil в случае успешного парсинга, и nil + инстанс ошибки с описанием проблемы в случае фейла?


                                    1. PsyHaSTe
                                      29.12.2018 13:18

                                      1. Зачем вам логика парсинга JSON в конструкторе этого самого «меча»?

                                      Ну, должен же меч как-то быть загружен. Обычная плагинная система — сначала скайрим идентифицирует моды, в частности «меч», потом говорит всем модам «загружайся», ну и дальше что-то пошло. Процесс загрузки состоит как раз из парсинга конфига, определения характеристик, и вот этого всего.

                                      Чем случай «json-файл с конфигом отсутствует» в вашем конкретном случае отличается от «json-файл с конфигом не читается» или «json-файл с конфигом содержит некорректные данные»?

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

                                      Вот смотрите, у вас же, видимо, на весь «МойСкайрим_v1.0Beta» несколько больше одного конфига? У вас же, надеюсь, конфиг самой игры, содержащий разрешение экрана, режим работы, настройки управления и т.д. и конфигурация ресурсов игры — это разные файлы? Причем, вероятно, на каждый «убер-меч» конфиг свой? Или вы враг себе и игроку?

                                      У каждого мода свой конфиг, у скайрима свой. Не понлял, к чему это было сказано.

                                      Ну вот объясните мне, что, собственно, должна ваша библиотека по загрузке конфигурации делать такого, чего не может стандартная json-парсилка, которая возвращает инстанс объекта + nil в случае успешного парсинга, и nil + инстанс ошибки с описанием проблемы в случае фейла?

                                      Мержить конфиги, например. Ну или тот же релоад, как уже упоминалось. Чтобы поправили в конфиге значение, сохранили, а у вас в игре без перезагрузки сразу обновились характеристики.


                  1. qrKot
                    28.12.2018 09:22

                    Библиотеку чего, простите, в контексте работы с конфигом?


              1. qrKot
                28.12.2018 09:12

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


          1. qrKot
            27.12.2018 16:17

            Возвращать nil вместо объекта конфигурации?


            А вы, видимо, предлагаете возвращать заведомо некорректную конфигурацию и с ней работать? Как бэ, определение того, «что пошло не так» и получение непосредственно конфигурации, с которой нужно работать — вопросы ортогональные.

            Информацию о том, что пошло не так, вам, вероятно, вернуть надо, в каком-то виде (бодро отрапорторвать в консоль, вернуть информативное описание ошибки, щедро наложить этой информации в лог — вариантов «мульён»). Но это не отменяет того факта, что «если что-то пошло не так», возвращать «хотя бы что-нибудь, пусть поломатое, но хоть такое» и продолжать работу сервиса с заведомо косячным конфигом… Ну, как бы, за такое тапком с сервера гнать надо, имхо.

            Собственно, вы упираете на «файл конфига есть, просто при чтении возникла какая-то ошибка». Чем, простите, с точки зрения корректности конфигурации, отличается ситуация «файла конфига нет» от «файл конфига не читается» или «в файле конфига какая-то труднообъяснимая ересь»? Результат с точки зрения сервиса в целом один: конфига нет.


            1. PsyHaSTe
              27.12.2018 16:28

              А вы, видимо, предлагаете возвращать заведомо некорректную конфигурацию и с ней работать?

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

              Подход Go подталкивает к обработке ошибок, а не на отсылку их выше в надежде, что кто-нибудь разберется.

              Пытаюсь понять, как должен в этой ситуации код «разобраться на месте, а не отсылать наверх».


              1. powerman
                27.12.2018 20:20
                -1

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


                Да, очень часто её нужно просто прокинуть наверх, но, тем не менее, хоть это и самый частый вариант, делать его вариантом "по умолчанию" (как очень часто используют исключения) это плохая идея, именно потому, что позволяет вообще не задумываться об обработке ошибок до самого последнего момента. И да, если разработчик будет механически везде вставлять if err != nil { return err } то это будет ровно то же самое — он не задумывается об обработке ошибок, и просто тупо набирает бессмысленный код. Тем не менее, сама необходимость этот код набирать, и потом читать на ревью — увеличивает вероятность, что кто-нибудь всё-таки задумается, насколько этот код корректен в случае ошибки конкретно в этом месте.


                Конечно, всем хочется набирать меньше тупого шаблонного кода, но нельзя это делать ценой потери такого полезного качества языка как подталкивание программистов задумываться о корректной обработке ошибок. Более детально почитать про это можно в описании нового синтаксиса обработки ошибок в Go2 через check/handle.


                1. PsyHaSTe
                  27.12.2018 21:06

                  Суть фразы «Подход Go подталкивает к обработке ошибок, а не на отсылку их выше в надежде, что кто-нибудь разберется.» в том, что нужно в каждой конкретной точке задуматься, как именно здесь нужно обработать ошибку.

                  Я и так задумываюсь, нужен ли для ошибки контекст, или нет. Особенно, если нет возможности обернуть всё в какой-нибудь сверхтип, в который можно скастовать из любого, а нужно явно перечислять, что конкретно может пойти не так, и соответственно выбирать из перечня, к какой из ошибочных ситуаций относится потенциально данная.

                  Да, очень часто её нужно просто прокинуть наверх, но, тем не менее, хоть это и самый частый вариант, делать его вариантом «по умолчанию» (как очень часто используют исключения) это плохая идея

                  в любом ЯП хорошая идея делать дефолтным вариантом самый частоиспользуемый.

                  И да, если разработчик будет механически везде вставлять if err != nil { return err } то это будет ровно то же самое — он не задумывается об обработке ошибок, и просто тупо набирает

                  Так он так и будет делать. В идеале, сделает сниппет, который по ctrl+ec будет вставлять этот код.

                  Тем не менее, сама необходимость этот код набирать, и потом читать на ревью — увеличивает вероятность, что кто-нибудь всё-таки задумается, насколько этот код корректен в случае ошибки конкретно в этом месте.

                  Люди — не идиоты. Например «баннерная слепота» туда же, люди автоматически игнорируют знакомые идиомы и паттерны. Это хорошо видно в исследованиях влияния подсветки кода на скорость распознавания и понимания.


                  1. powerman
                    27.12.2018 22:26

                    Суть в том, что нужно ручками сделать хоть что-то в том месте, где надо обработать ошибку. У меня есть сниппеты, которые вставляют такие конструкции. А ещё можно ошибку тупо загасить приняв её в _ вместо переменной. Но и подчёркивание, и комбинацию для сниппета надо набрать. А значит — задуматься, какой из типичных сниппетов или подчёркивание подходит для обработки текущей ошибки. Что и требовалось. Не важно, сколько кнопок нужно нажать на клавиатуре, важно что их в принципе нужно нажать, и перед этим задуматься, какие именно.


                    Плюс к этому, на ревью очень плохо отслеживаются ошибки связанные с отсутствующим кодом, но зато намного лучше отслеживаются ошибки в добавленном/изменённом коде. Если для обработки каждой ошибки обязательно надо что-то в код добавить, пусть даже это одно подчёркивание или "типичный" блок прокидывания ошибки выше — это более заметно на ревью, даже с учётом "баннерной слепоты", чем отсутствие этого кода вообще.


                    1. PsyHaSTe
                      27.12.2018 23:27

                      Не важно, сколько кнопок нужно нажать на клавиатуре, важно что их в принципе нужно нажать, и перед этим задуматься, какие именно.

                      В тысячный раз над этим задумываться перестаешь. Проверенно типовыми throw new Exception() и прочими фрагментами.


                      1. powerman
                        27.12.2018 23:42

                        Никто не в силах помешать делать глупости или писать код бездумно. ЯП может просто способствовать более правильному отношению либо потакать менее правильному. Смысл — зачем это делается именно так — я пояснил. Конкретная реализация — как сделано сейчас, или как в будущем будет сделано через handle/check — не имеет принципиального значения, пока сохраняется изначальный смысл. Придумаете более классную реализацию — welcome, сейчас как раз идёт обсуждение handle/check, и у Вас есть реальный шанс повлиять на дизайн языка.


                        1. PsyHaSTe
                          28.12.2018 01:33

                          Да нет, я просто не думаю, что программистов надо считать идиотами. Забывчивыми — да, ошибающимися — возможно. Но не глупыми ни разу.


                          Вот есть тропинка — как люди делают. Там, где в других языках на этом месте кладут асфальт (добавляют в язык фичу, которая облегчает этот паттерн), в го ставят забор, чтобы человек точно задумался, нужно ли ему срезать угол. Даже звучит глуповато.


                          1. powerman
                            28.12.2018 04:10

                            Думаю, здесь очень тонкая грань, между главной фичей того же раста, и аналогичными фичами Go — и то и другое предназначено для того, чтобы защитить программиста от его же ошибок, и то и то можно обойти через unsafe (если уверен, что умнее компилятора). Где провести эту грань — вопрос сугубо индивидуальный, множество программистов считает, что даже строгая типизация уже "за гранью" и что не надо считать их идиотами и мешать им складывать строки с числами.


                            1. PsyHaSTe
                              28.12.2018 12:00

                              Ну возможно. Если бездумно лепить unsafe в расте то можно добиться похожего эффекта, как сниппет для IDE.


              1. qrKot
                28.12.2018 09:47

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


                Вот именно так и «идиоматично» для Go.

                Шаг #1: получаем конфиг (config, err := conf.GetConfigFromFile('/file/path/here'))
                Шаг #2: смотрим на err, если оно не nil, предпринимаем действия по обработки (заметьте, именно в вызывающем коде)
                Шаг #3: если конфиг все еще считается валидным, создаем инстанс сервиса (service, err := serv.NewInstance(&config)), скармливая готовый конфиг внутрь. Если сервис не смог «подняться» — смотрим в ошибку, принимаем решение, что делать дальше (опять же на стороне вызывающего кода)


  1. gnomeby
    26.12.2018 14:50
    -6

    Итого:
    1. Нет подсветки кода в документации. — Ну нет и нет, это не тянет на недостаток.
    2. Политика. — Высосано из пальца. Может быть так, а может быть иначе. Неявное разделение есть в любом языке и проекте.
    3. Пакеты и GOPATH. — уже на дворе 1.11 и это исправили.
    4. Обработка ошибок в Go. — скоро завезут гораздо более приятную.

    Итого вкусовщина на основе частично-устаревшей информации. Go есть за что поругать, но оригинальные авторы статьи даже не заморачивались этим вопросом.


    1. nexmean
      26.12.2018 15:00
      +2

      Политика. — Высосано из пальца. Может быть так, а может быть иначе. Неявное разделение есть в любом языке и проекте.

      Зато есть весьма явное высказывание о Golang от Роба Пайка о том для кого и зачем делался язык.


      Пакеты и GOPATH. — уже на дворе 1.11 и это исправили.

      Вы во всех своих проектах уже на модули переехали?


      Обработка ошибок в Go. — скоро завезут гораздо более приятную.

      Скоро в хаскель линейные типы завезут. Тем не менее пока это нельзя считать достоинством хаскеля.


      1. gnomeby
        26.12.2018 16:08
        -2

        Зато есть весьма явное высказывание о Golang от Роба Пайка о том для кого и зачем делался язык.

        Автор языка может говорить всё что угодно. Произведение очень часто живёт самостоятельной жизнью отдельно от автора.
        Вы во всех своих проектах уже на модули переехали?

        Мы уже готовы переехать и сделаем это в ближайший месяц. Но пока и от GOPATH не сильно страдаем и vendor mode используем и как-то нормально.

        Про обработку ошибок я упоминаю только потому, что довольно скоро пол этой статьи потеряет актуальность. Тем не менее в статье это подаётся как истина последней инстанции.


        1. mr_bag
          26.12.2018 16:28

          Я правильно понимаю, что в языках с tuple и exception можно использовать и подход Go и ловить исключения?


          1. gnomeby
            26.12.2018 18:33

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


          1. nexmean
            26.12.2018 18:53

            Обычно для обработки ошибок в языках, где для этого не принято использовать исключения используются типы-суммы, например Result из Rust или Either из Haskell.


      1. youROCK
        26.12.2018 16:13

        Зато есть весьма явное высказывание о Golang от Роба Пайка о том для кого и зачем делался язык.

        Я кстати не очень понимаю, почему всех так задевает, что Роб Пайк отзывается о молодых программистах в Google, как о не самых умных и опытных :)?

        Программирование — очень бурно развивающаяся профессия и с большим количеством новичков, причём зачастую без профильного образования или без образования вообще. Даже Rust, который не такой сложный, как, например, Haskell, как-то не получил такой популярности, как Go.

        Можно возразить, что «настоящие программисты» никогда не будут писать на Go, потому что он «для дураков», но это я бы оставил на совести того, кто так думает. Да, безусловно, доступность Go это и недостаток, потому что привлекает много новичков без опыта. Но и достаточно высококвалифицированные программисты тоже его используют, и с этим спорить достаточно сложно (хотя, например, тот же Docker я хорошим примером не считаю).


        1. potan
          26.12.2018 16:35
          +3

          Rust, который не такой сложный, как, например, Haskell
          Только все наоборот — Rust сложный язык, предназначенный для решения сложных задач в ограниченных ресурсах по памяти и производительности, а базовый Haskell (если не углубляться в редко используемые темы, вроде GADT) очень простой, с минимумом необходимых для работы концепций.


          1. 0xd34df00d
            26.12.2018 18:28
            +1

            Только сейчас куда ни ткни, везде то профункторы (lens), то попытка прикинуться завтипизированным языком (singletons).


          1. PsyHaSTe
            27.12.2018 15:20

            Раст намного более привычный, чем хаскель. Во-первых он императивный. Во-вторых С-подобный. Кроме лайфтаймов в нем нет ничего особо непривычного.


            1. potan
              27.12.2018 19:12

              Там есть алгебраические типы, их же гошники не способны освоить!


        1. mr_bag
          26.12.2018 16:36

          Вся продукция HashiCorp есть оправдание существования Go! :)


        1. TargetSan
          26.12.2018 16:59
          +2

          Проблема в том, что в ответ на почти любые претензии о косяках или явном примитивизме получаешь ответ "Ты ничего не понимаешь, малыш! Так надо!". Помню как на вопрос почему вообще допустили такой косяк как non-nil nil interfaces, я получил ответ именно в таком стиле, с жонглированием значения термина "указатель". Ссылку, к сожалению, уже не найду. Ответ был не от Пайка если что.


  1. freecoder_xx
    26.12.2018 15:03
    +4

    такие как Rust,… — тоже выдают предупреждения, где это возможно

    В Rust будет не предупреждение, а ошибка компиляции, если вариативный анализ тип-суммы окажется неполным.


  1. ertaquo
    26.12.2018 16:01
    +1

    Чего лично мне не хватает в Go:
    1. Значений для аргументов функций и свойств структур по умолчанию.
    Согласитесь, было бы гораздо удобнее писать так:

    type Qwerty struct {
      A int = 10
      B string = "cdef"
    }
    
    func Zxc(A string, B integer = 456) {
    }

    2. Более прямой инициализации свойств «вложенной» структуры при наследовании.
    Сейчас надо сделать как-то так:
    type A struct {
      One int
    }
    type B struct {
      A
      Two int
    }
    ...
    B{
      A{
        One: 1,
      },
      Two: 2,
    }


    1. powerman
      26.12.2018 20:11

      Согласитесь, было бы гораздо удобнее писать так:

      Писать — да. Читать — нет. А легко читать намного важнее.


      Более прямой инициализации свойств «вложенной» структуры при наследовании.

      Так можно делать, просто не надо смешивать инициализацию по имени и по позиции: https://play.golang.org/p/5Qihnbmv1Gi


      1. ertaquo
        26.12.2018 20:28

        Писать — да. Читать — нет. А легко читать намного важнее.
        В очень многих языках есть подобный синтаксис. Почему бы и нет? Зато сейчас приходится писать отдельную функцию для инициализации этих полей значениями по умолчанию.
        Так можно делать, просто не надо смешивать инициализацию по имени и по позиции:
        Я имел в виду такое:
        B{
          One: 1,
          Two: 2,
        }


        1. powerman
          26.12.2018 21:03

          В очень многих языках есть подобный синтаксис.

          У очень многих языков есть проблема с читабельностью кода. Значения по умолчанию — это палка о двух концах, они нужны для того, чтобы опускать значения, вместо того чтобы задавать их явно. Писать такой код, очевидно, проще — меньше набирать надо, код получается компактнее, и в момент написания в голове есть весь необходимый контекст, включая опущенные значения "по умолчанию". А вот читать этот код позднее намного сложнее, потому что полного контекста в голове уже нет, и о наличии опущенных значений сначала надо догадаться, а потом посмотреть эти значения где-то в другом файле.


          Я имел в виду такое:

          Может добавят в будущем, а пока можно так:


          var b B
          b.One = 1
          b.Two = 2


    1. gudvinr
      26.12.2018 23:39

      Первый пример не очень хорош тем, что по-хорошему, за неименованые константы стоит ругать, однако, стоит разобраться, зачем это надо.


      • В Go нет такой ситуации, как, например, в C, когда в переменной может храниться что угодно и надо принудительно проинициализировать нулём, пустой строкой и т.п.
      • Как быть с типами, которые не позволяют создавать константы?
        • Инлайновое объявление какой-нибудь структуры или слайса превратит код в лапшу (и убьёт удобство)
        • Если позволять в качестве значений по-умолчанию вставлять изменяемые переменные, то непонятно, в какой момент вычислять дефолтное значение

      По-второму всё довольно очевидно потому что в Go нет наследования, а есть композиция. И если понимать, что это именно вложенный самостоятельный объект, а не классическое наследование, то получается, что прямее некуда.
      Например, можно делать так:


      type B struct {
          *A
          Two int
      }

      И в этом случае B.A по умолчанию не инициализируется и nil. И для указателя нормальное дело быть инициализированным в nil по-умолчанию, так что прямее невозможно сделать как раз потому, что это и не наследование вовсе.


  1. KirEv
    26.12.2018 16:42
    +5

    ээ… и это все?

    I've gotten pretty familiar with it.


    Если это действительно так, то не упомянуты вещи страшнее отсутствия привычных исключений и лапшевидного реагирование на ошибки:
    — дедлоки каналов, по невнимательности\неопытности заруинит приложение и вообще отобьет охоту иметь дело с го
    — интерфейсы (они вообще развязывают руки для любого извращения)
    — горутинами можно злоупотреблять, компилятор не выдаст предупреждение, если в какой-то горутине не закрывать дескриптор ресурса (файл, соединение, етс.)
    — йота — странный модификатор констант, оно мне больше напоминает «финт для продвинутых», пользы от наличия enum больше.
    — неоднозначная однозначность… вроде как навязывают единый стиль, даже gofmt все в едино сводит, и даже в последних версиях пустые строки научился удалять, но опять же возможность делать одно и тоже несколькими способами вносит разногласия:

    if err := f(); err != nil {
    ...
    }
    err := f()
    if err != nil {
    }
    v1 := new(s)
    v2 := &s{}
    


    и всякое такое…

    особенно раздражает возможность инициализировать переменную в любом месте… хоть через `var`, хоть через `:=`, кроме того область видимости переменной может быть лишь в пределах условия\цикла (тоесть одно и тоже имя для переменной использовать многократно в разных конструкциях в пределах одной функции)…

    И куча другого субъективного, как по мне, го:
    1. позволяет писать синтаксически запутанный код
    2. порог вхождения не так низкий…
    3. и хочется и колется… но продолжаю писать на го…

    PS: честно сказать, начитавшись обзоров и т.п., загорелся, это было года 3 назад… потом еще 1 год «был занят»… и вот доходит… после состояния эйфории, десятков проектов и сопутствующей головной боли: ожидал внятный язык, до сих пор иногда сложно определиться какими правилами пользоваться для консистентности кода, а когда работаем над проектом больше равно двух — начинаем долго спорить о правилах…

    PS: не хотел задеть чувства верующих, пишу и буду ближайшее время писать на го, но на волне хайпа он собственно выехал, а есть же другие языки, примерно ровесники го, о которых гошники и не знаю, потому что не гугл.


    1. gudvinr
      27.12.2018 00:01
      +1

      дедлоки каналов, по невнимательности\неопытности заруинит приложение и вообще отобьет охоту иметь дело с го

      Дедлоки в принципе возможны везде, где есть асинхронность, и в Go, в отличие от многих других экосистем, есть рейс-детектор в поставке по-умолчанию и отладка таких вещей намного проще, чем не в Go.


      интерфейсы (они вообще развязывают руки для любого извращения)

      Интерфейсами злоупотребляют в тех случаях, когда по каким-то причинам думают, что func f(interface{}) == template typename<T> f(T), а если так не делать (а так делать прямо не советуют в официальных ресурсах), то и проблем не будет


      горутинами можно злоупотреблять, компилятор не выдаст предупреждение, если в какой-то горутине не закрывать дескриптор ресурса (файл, соединение, етс.)

      Но незакрытые дескрипторы вызовут проблемы и если вы оставите их открытыми в любом другом языке. При этом, опять же, везде где происходит работа с чем-то таким, в документации используют что-то вроде defer XXX.Close().


      йота — странный модификатор констант, оно мне больше напоминает «финт для продвинутых», пользы от наличия enum больше.

      Это разве страшно? Он не приводит к запутанному коду или содержит слишком дикую магию, но, конечно, выглядит как костыль.


      возможность делать одно и тоже несколькими способами

      Строго говоря, не одно и то же:


      // область видимости err ограничена внутренностью if и не перезаписывает переменную err извне
      if err := f(); err != nil {  
          // ...
      }
      
      err := f() // область видимости err ограничена функцией, и дальше не может быть переопределена
      if err != nil {
          // ...
      }

      v1 := new(int) // так сделать можно
      v2 := &int     // так сделать нельзя


      1. 0xd34df00d
        27.12.2018 03:38
        +1

        Но незакрытые дескрипторы вызовут проблемы и если вы оставите их открытыми в любом другом языке.

        В языке с субструктурной типизацией не вызовут, компилятор ругнётся. В языке с RAII можно просто всё завернуть в класс с деструктором.


        Если вы дескрипторы правильно описали, конечно, но это снижает количество мест для совершения ошибки с N точек закрытия дескриптора до одной точки описания дескриптора.


        1. gudvinr
          27.12.2018 04:06

          Это верно, однако, и RAII — это не серебряная пуля. Чтобы в этом случае дескриптор закрылся — объект должен быть освобождён, а если он висит ожидая чего-то, то это не особо поможет.


          В том же Go проблемы с горутинами и ресурсами, в основном, связаны не с тем, что кто-то забывает вызвать Close (к тому же финалайзеры вызываются, когда os.File собирается GC, например. эдакий недетерминированный RAII), а из-за того, что условие выхода из горутины не определено.
          Про таймауты на http-соединения забывают намного чаще. А это, в принципе, мало зависит от языка.


          Можно возразить, мол, не во всех языках есть "легкие треды" и будете правы, там где приходится иметь пул потоков — он забьётся и всё колом встанет, а в Go будет расти память на висящих горутинах, пока процесс не умрет по OOM.


          1. TrueMaker
            27.12.2018 09:05
            -1

            а в Go будет расти память на висящих горутинах

            Для этого есть мониторинг, у нас всегда снимаются снэпшоты состояния работающих Go процессов и такие утечки ловятся моментально, как и множество других проблем которые можно определить по состоянию рантайма.


          1. 0xd34df00d
            28.12.2018 22:58

            Чтобы в этом случае дескриптор закрылся — объект должен быть освобождён, а если он висит ожидая чего-то, то это не особо поможет.

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


      1. scalavod
        28.12.2018 00:04

        > Дедлоки в принципе возможны везде, где есть асинхронность, и в Go, в отличие от многих других экосистем, есть рейс-детектор в поставке по-умолчанию и отладка таких вещей намного проще, чем не в Go.

        Не везде. Например в pony нет дедлоков за счёт специальных аннотаций типов.


    1. qrKot
      27.12.2018 12:47

      кроме того область видимости переменной может быть лишь в пределах условия\цикла (тоесть одно и тоже имя для переменной использовать многократно в разных конструкциях в пределах одной функции)…


      А скоуп-то вам чем помешал? Да, собственно, покажите мне язык, где сделано иначе?


  1. dchusovitin
    26.12.2018 18:51
    +1

    Обрабатывать ошибки в Go еще не удобнее, если необходимо выполнять разные условия в зависимости от ее типа.

    1. В большинстве случаев просто возвращается ошибка с текстом (fmt.Errorf, errors.New). В этом случае необходимо ее парсить.
    2. В библиотеке есть внутренние «типизированные» ошибки (var errInternal = errors.New(«package: internal»). Но они не экспортируются и так же приходится парсить.
    3. Экспортирует внутренние ошибки, сравнение по значению (err == io.EOF)
    4. Проверять ошибку на соответствию интерфейсу (Timeout)
    5. Возвращает свою структуру (net.OpError) с доп. контекстом. Делаем swtich err.(type).

    Например, пакет http возвращает их все, т.к. состоит из множества других пакетов. И если необходимо узнать причину возникновения ошибки (на уровне DNS, недоступность сервера, таймайты, ошибка прокси), то получается огромное ветвление на пару экранов, где идет сверка регулярными выражениями.

    Но этим грешат и сторонние пакеты, так и подходы в других языках.


    1. powerman
      26.12.2018 20:29

      Всё так. Я думаю, что эти ситуация вызвана гарантией обратной совместимости Go1 и поддержкой разных платформ, ошибки у которых бывают разными — поэтому существует множество ошибок, которые очень не хотелось делать частью публичного интерфейса стандартной библиотеки.


      Но абсолютное большинство сторонних библиотек и наш собственный код вполне может экспортировать все нужные ошибки и использовать интерфейсы или типы для группировки ошибок по классам (вроде Timeout). Так что за редкими исключениями вроде ситуации с errClosing всё не так плохо.


  1. Sabubu
    26.12.2018 18:58
    +1

    Что мне не нравится в Го:

    — отсутствие правил для распределения определений типов и функций по файлам. По идее пакет — это папка, но правила не говорят ничего о том, в какой файл надо помещать то или иное определение. При навигации в чужом коде не понять, где находится то или иное определение. При написании кода не понять, куда их помещать. Сделали бы каждый файл отдельным пакетом тогда. Вот в PHP — в 20 раз проще: каждый класс в своем файле.

    — непонятная ситуация с менеджером пакетов. Почему нельзя, как в PHP, просто сделать composer install? Зачем устроили чехарду с GOPATH? Вот удовольствие, всю иерархию папок в /tmp создавать. И непонятно как выложить пакет на гитхаб. Самый работающий вариант — просто скачать зависимости и засунуть в папку.

    — дурацкая идея с большими буквами для обозначения экспорта. Половина функций с маленькой буквы, половина с большой, и надо постоянно вспоминать, как именно пишется та или иная.

    — невозможно нормально вызывать код на Си, так как у Го есть свои потоки, которые параллельно что-то там делают и мешают коду на Си. Почему нет функции для отключения всей этой ерунды, когда нужно? Пытаешься навесить ограничения через seccomp() и все отваливается. Ну и сам CGO так себе — надо как-то извращенно в комментариях указывать инклуды и флаги компилятора. Причем ведь, небось, не портабельно и не с любым комилятором это работает.


    1. powerman
      26.12.2018 20:42
      -1

      При навигации в чужом коде не понять, где находится то или иное определение.

      Учитывая отличную поддержку go to definition во всех редакторах/IDE для Go — это никогда не является проблемой, мне давно всё-равно в каком файле написано объявление, если я могу туда моментально попасть нажав gd на идентификаторе в vim.


      А вообще, если в пакете слишком много файлов, и их названия не помогают понять что в них — то либо пора разделить пакет на несколько по-меньше, либо научиться по-лучше выбирать имена для файлов (впрочем, выбор хороших имён всегда был одной из самых сложных проблем в программировании, так что это не проблема Go).


      Зачем устроили чехарду с GOPATH?

      Затем, что традиционные подходы к управлению пакетами не устраивали. Сейчас эта проблема уже (почти) в прошлом, поддержка модулей уже добавлена, осталось дождаться пока подтянутся утилиты вроде gorename чтобы работа вне GOPATH не создавала неудобств.


      и надо постоянно вспоминать, как именно пишется

      Вообще-то достаточно отдавать себе отчёт в том, что является публичной частью интерфейса текущего пакета, а что нет. Если Вы не уверены в том, какие функции/типы пакеты экспортируются, а какие нет — у Вас проблема намного серьёзнее, чем вспомнить регистр первой буквы идентификатора.


      невозможно нормально вызывать код на Си

      Это общая проблема всех языков, которые не Си. По крайней мере всех, на которых мне это приходилось делать.


    1. qrKot
      27.12.2018 13:10
      -1

      Иииии… Приз за самый странный список претензий к Go уходит господину с ником Sabubu.

      По идее пакет — это папка


      И не по идее тоже.

      но правила не говорят ничего о том, в какой файл надо помещать то или иное определение.


      Собственно, кто вам мешает их написать и строжайшим образом придерживаться?

      При навигации в чужом коде не понять, где находится то или иное определение


      В теории, мне рассказывали, что есть такая штука, как go to definition или, на крайняк, текстовый поиск в пределах папки. Но это, конечно, не точно.

      Сделали бы каждый файл отдельным пакетом тогда


      Не надо, пожалуйста. Go достаточно многословен в некоторых местах, и заменить поиск по набору относительно коротких файлов поиском по простыне на пару-тройку тысяч строк… Вы сами этого не хотите, поверьте.

      непонятная ситуация с менеджером пакетов


      go get = git clone. Куда уж понятнее, чесслово?

      Почему нельзя, как в PHP, просто сделать composer install?


      Чоэта нельзя? dep init && dep ensure? Да там сильно больше одного варианта, если честно.

      Зачем устроили чехарду с GOPATH?


      Чтобы не повторять историю с node_modules, вероятно.

      Вот удовольствие, всю иерархию папок в /tmp создавать.


      Честно говоря, не самое подходящее место для GOPATH, имхо.

      Самый работающий вариант — просто скачать зависимости и засунуть в папку.


      Модули же подвезли, они именно это и делают. Только дубликатов избегают, например, что тоже ценно, имхо. А до модулей govendor, dep были, и еще кто-то, стопудово, кем я не пользовался.

      Половина функций с маленькой буквы, половина с большой, и надо постоянно вспоминать, как именно пишется та или иная.


      Импортированная извне — с заглавной (а перед ней еще имя пакета, обычно), своя внутренняя — со строчной.

      невозможно нормально вызывать код на Си, так как у Го есть свои потоки, которые параллельно что-то там делают и мешают коду на Си


      Ну вот это вообще странная претензия. Из языка A сложно работать с кодом на языке B. Покажите, пожалуйста, где это не так.

      Почему нет функции для отключения всей этой ерунды, когда нужно?


      Когда вам нужно отключить всю эту ерунду, вероятно, вам стоит писать на C. Потому что с отключенной «ерундой» Go именно в C и превращается. А у C еще и с интеропом с другим кодом на C проблем меньше.

      не с любым комилятором это работает


      Ну, как бы, у Go официально один компилятор…


  1. 411
    26.12.2018 20:20

    Читаю очередную статью о недостатках Go и со всем согласен. А где статьи о плюсах Go? Интереса ради бы почитал. Не считаю язык хорошим, но понять бы, что другие в нём находят.


    1. Laney1
      26.12.2018 21:17
      +3

      легко писать, легко читать, легко собирать, легко деплоить на сервер, легко и очевидно вообще все. Этот язык предназначен не для написания восторженных статей о его фичах, а для того чтобы его использовали.


      Я помню, как несколько лет назад Умпутун издевался над только что вышедшим Го в своем "Радио-Т". С точно такими же аргументами: наследования нет, дженериков нет, исключений нет, какой-то язык из 80-х, срамота ходячая. Результат: https://github.com/umputun?tab=repositories


      1. balsoft
        26.12.2018 21:39
        +3

        Так можно сказать про любой язык:

        легко писать, легко читать, легко собирать, легко деплоить на сервер, легко и очевидно вообще все.

        Вот только на поверку оказывается, что иногда проще даже на Rust написать, чем на Go с его вырвиглазной и нечитабельной обработкой ошибок, странными naming conventions (вкупе с отсутствием нормальных IDE кроме жидбрейновской), и возможностью выстрела в ногу с горутинами. В Rust безопасность и эффективность кода получается выше, при этом код не сильно менее читабелен.


        1. qrKot
          27.12.2018 13:16
          -2

          иногда проще даже на Rust написать


          Вы же понимаете, что ключевое слово — «иногда». Прям, я бы сказал, сильно иногда.


        1. PsyHaSTe
          27.12.2018 15:35

          при этом код не сильно менее читабелен.

          Прикладной код вообще не менее читабелен, а скорее более.


          Я в соседней статье попросил показать мне конкретные недостатки моей реализации телеграм-клиента на расте, из-за которой непонятно, что происходит. Пока что тишина...


          1. balsoft
            27.12.2018 16:30

            Мне судить сложно, я в обоих технологиях на уровне джуна. Сужу по опен-сорцу.


      1. alff31
        27.12.2018 12:42

        Несколько лет назад не знал про Радио-Т, но сейчас умпутун там главный проповедник Go


      1. serge-phi
        27.12.2018 15:24

        Повзрослел человек, сменились ценности. Вместо интереса процесса стал важнее простой и надежный результат.


    1. powerman
      26.12.2018 21:46
      +5

      Если коротко, то на нём просто, быстро и приятно писать надёжный и читабельный код. Если не так коротко, то (в случайном порядке):


      • простой язык, что даёт возможность без труда выучить его "от и до", на 100%
      • гарантия обратной совместимости Go1, что даёт возможность тратить минимум времени на поддержку старого кода
      • отличный вспомогательный инструментарий, как "из коробки", так и сторонние утилиты (тут конкретику можно долго перечислять, из основного: тесты, документация, управление пакетами, мощные линтеры, кодогенерация, детектор race conditions, …)
      • очень простая кросс-компиляция и отличная портабельность стандартной библиотеки, что сильно упрощает разработку кросс-платформенных приложений
      • один бинарник, часто статический, что упрощает деплой как микросервисов, так и кросс-платформенных утилит
      • строгая статическая типизация с выводом типов, что позволяет получить соответствующие плюшки без того, чтобы вручную постоянно уточнять все типы в коде
      • горутины и каналы, что даёт возможность писать обычный синхронный код (как правило без мьютексов и коллбэков) и при этом обрабатывать миллионы параллельных соединений задействуя все CPU, имея при этом на порядок реже типичные для многопоточного кода сложные проблемы с дедлоками и race conditions
      • интерфейсы (которые, к сожалению, многие не понимают), которые позволяют проще и элегантнее проектировать код
      • defer, что упрощает контроль за освобождением ресурсов
      • отсутствие наследования, что тоже способствует написанию более ясного кода
      • регулярки RE2, что гарантирует предсказуемое время их выполнения
      • безопасный, в плане отсутствия ручного управления памятью…
      • … но при этом достаточно низкоуровневый, чтобы можно было при необходимости вручную оптимизировать код вплоть до выделения памяти
      • единый стиль оформления кода везде, что упрощает чтение кода и экономит время на согласование стиля в каждом проекте
      • мощная стандартная библиотека, что даёт возможность обходиться без дополнительных больших и сложных фреймворков в большинстве проектов
      • под линух есть возможность писать аналог обычных скриптов на Go (т.е. выполнимый файл с исходником на Go а-ля ~/bin/something.go, который можно запускать как обычную команду something.go)
      • очень быстрая компиляция, вроде бы мелочь, но как посмотришь на C++ то становится ясно, что не такая уж это и мелочь
      • в отличие от некоторых других отличных языков — на нём легко найти работу


      1. Denis631
        27.12.2018 13:05

        быстро и приятно

        ну с приятностью я б поспорил. Для меня Go это необходимое зло


      1. scalavod
        28.12.2018 00:34
        +1

        > строгая статическая типизация с выводом типов

        Честно, вывод типов в go достаточно слабый.

        Простейший пример на go.
        Простейший пример на rust.

        В аргументе c функции call указаны все необходимые типы для колбэка. При этом, в коде на go всё равно приходится их указывать, тогда как в коде на rust в этом уже нет необходимости.


    1. wmns
      27.12.2018 13:05
      +1

      Читаю очередную статью о недостатках

      «Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует.
      © Бьерн Страуструп»


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


      1. ivan386
        27.12.2018 13:13

        Ну так ругаются и хвалят его наверно те кто его изучают добровольно или принудительно. Остальным то какое до него дело?


      1. kalyukdo
        27.12.2018 14:25

        Все очень просто,
        отрицание, равнодушие, принятие

        У меня так с каждой технологией, с начала хаю, потом становится ровно, а потом слезть не могу, а с Go я сейчас между равнодушием и принятием


  1. Bojczuk
    26.12.2018 20:20
    +1

    За что я люблю язык Go:
    1. На него есть спрос у бизнеса.
    2. За него хорошо платят.


    1. aensidhe
      27.12.2018 10:09
      -3

      Хорошо — это сколько?


      1. qrKot
        27.12.2018 13:19

        Хорошо — это в среднем по палате дешевле, чем за Java и C++, но дороже, чем за PHP.


        1. ganqqwerty
          27.12.2018 14:26

          но ведь с PHP можно уйти на JS и мимикрировать под фуллстека — и будут платить совсем хорошо!


          1. gnomeby
            27.12.2018 14:36

            Не факт, что это будет длиться долго.


            1. ganqqwerty
              27.12.2018 14:40

              Совершенно точно, что это НЕ будет длиться долго, если не повторится бум базвордов, как несколько лет назад. Нам критически нужны новые базворды от авторитетных компаний: что-нибудь сравнимое с tree-shaking, SPA, PWA, typescript, RxJS, войны css-препроцессоров, несколько мажорных изменений в ES, и самое главное — больше короткоживущих фреймворков: оптимально, чтобы фреймворки устаревали за полгода-год. Только так можно сохранить оплату за формошлепство на текущем уровне, только так мы можем спасти индустрию от наплыва умников из других языков.

              Если большая тройка фреймворков будет продолжать жить еще год-два, зарплаты у фронтов неизбежно упадут на уровень зарплат пхпшников пятилетней давности.


          1. qrKot
            27.12.2018 15:26

            Лучше уходите на JS client-side, не надо пилить на нем серверную часть. Тогда я хотя бы не буду смотреть на вас с осуждением.

            Ну, либо просто уходите)


            1. ganqqwerty
              27.12.2018 15:39

              не нуачо? Разве не были убедительными аргументы продвигателей ноды, о том, что джс за время войны браузеров стал ого-го какой язык, и надо всего-то присобачить к нему библиотку врапперов системных вызовов и что-то няшное для работы с БД? Все поверили, теперь вот радуемся результату.


              1. isden
                27.12.2018 15:56

                А что не так с нодой? Накатываем typescript и радуемся жизни :)


                1. qrKot
                  27.12.2018 16:19
                  +1

                  С нодой… с нодой почти все не так, имхо.

                  Хотя, вижу, вы тоже на позитиве.


                  1. isden
                    27.12.2018 16:25

                    Нет, к сожалению, я трезв.
                    У ноды есть проблемы (на мой личный взгляд), куда же без них. Но не сильно критичные, и писать, скажем, на es6/esm (а в es7 еще много вкусного сахара везут) или даже на ts вполне приятно.


        1. aensidhe
          27.12.2018 18:57

          А. Ну то есть ровно столько же (плюс-минус), сколько и за нормальные языки. На которые тоже есть спрос у бизнеса. Странный плюс, который у всех плюс.


          1. qrKot
            28.12.2018 09:55

            Разве кто-то говорил о плюсах? Вы спросили: «сколько». Я вам ответил. Плюсы там в другом, впрочем, как и минусы.


  1. isden
    26.12.2018 20:40

    > if err != nil {

    Эм, а я тупо делал как-то вот так О_о.
    По-моему, что-то такое в getting started написано было, и я не очень понимаю, почему все продолжают эти портянки тянуть в каждой статье про go :(

    func (c *Config) load() {
        file, err := ioutil.ReadFile("config.json")
        failOnError(err, "Cannot open conf file")
        err = json.Unmarshal(file, &c)
        failOnError(err, "Cannot decode json")
    }
    
    func failOnError(err error, msg string) {
        if err != nil {
            log.Fatalf("%s: %s", msg, err)
            panic(fmt.Sprintf("%s: %s", msg, err))
        }
    }
    


    1. balsoft
      26.12.2018 21:40
      +3

      Потому что panic — не универсальная реакция на любую ошибку.


      1. isden
        26.12.2018 21:51
        +1

        Ну добавьте еще хелпер anythingOnError() с нужными действиями и все.
        Есть еще подходы вот вроде такого.
        Моя непонятка была в куче портянок с «if err != nil {» в почти каждой статье, тогда как один из базовых принципов разработки — это вынос портянок однотипного кода в функцию.


        1. balsoft
          26.12.2018 21:55

          Так в том-то и дело, что обработка ошибок — очень редко однотипный бойлерплейт, чаще всего каждой ошибке — свой подход (разве что их можно логать все, в остальном они различны).


          1. isden
            26.12.2018 22:02

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


          1. TheShock
            27.12.2018 16:51

            Так в том-то и дело, что обработка ошибок — очень редко однотипный бойлерплейт, чаще всего каждой ошибке — свой подход

            Почти всегда на практике обработка ошибок — однотипный бойлерплейт и тогда очень иногда это не так. Посмотрите любую либу на гоу.


            1. balsoft
              27.12.2018 17:08

              Если у вас *ошибки* обрабатываются однотипным бойлерплейтом — у меня для вас плохие новости. Ошибки должны быть правильно обработаны в каждом конкретном случае, иначе от них нет вообще никакого толка (можно тогда в любой ситуации panic делать).


              1. TheShock
                27.12.2018 17:09

                у меня для вас плохие новости

                Доктор, скажите прямо, какой диагноз?

                Ну вот серьезно. Какие именно новости? Скажите, чем это плохо


                1. balsoft
                  27.12.2018 17:14

                  Плохо это тем, что ваше приложение должно выводить информативные сообщения об ошибках, пытаться исправить ошибки, если это возможно, а ещё не падать в случае чего от непроинициализированного ресурса.


                  1. TheShock
                    27.12.2018 17:23

                    То есть код, который сотни раз подряд делает однотипное return err — плохой?


                    1. balsoft
                      27.12.2018 17:28

                      Зависит от контекста. Если это библиотека — скорее всего нет. Если это приложение — скорее всего да. Но тут есть ещё много нюансов.


                      1. TheShock
                        27.12.2018 17:29

                        То есть куча

                        if err != null {
                          return err
                        }

                        в библиотеке — это, по вашему, норм?


                        1. balsoft
                          27.12.2018 18:05

                          Для го — норм. Просто так — не норм.


                        1. Edison
                          27.12.2018 19:28

                          только


                          if err != nil {
                            return err
                          }


                        1. qrKot
                          28.12.2018 09:56

                          if err != null


                          null — не норм. Замените на nil, и тогда норм.


        1. TheShock
          27.12.2018 05:23

          Ну добавьте еще хелпер anythingOnError() с нужными действиями и все.

          Но ведь если там не будет паники, то код продолжит выполнение, так что такой прием подходит только для паники


          1. isden
            27.12.2018 10:33

            «Пишите код» :)
            Вы правда хотите чтобы я вас начал учить как писать код на go? :)
            Вот чесслово, в документации и нескольких статьях все принципы и концепции прекрасно описаны, все раскуриваются за пару-тройку вечеров и потом пишется как надо под ваши потребности.
            Я не говорю, что обработка ошибок в частности и язык вообще идеальны. Есть вещи, которые лично мне не нравятся. Но все эти особенности можно просто знать и писать код с их учетом.
            ЗЫ:
            Вот не понимаю людей «а почему в языке Х сделано не так как в моем любимом У? язык Х — говно».


            1. DexterHD
              27.12.2018 11:26

              А самое главное, почему просто не иcпользовать любимый Y. Так как будто все вокруг заставляют использовать Х. :)


            1. TheShock
              27.12.2018 16:00

              Я писал на го полгода. Я лишь говорю, что ваш совет о anythingOnError не работает


              1. isden
                27.12.2018 16:22

                Это был дженерик совет :)
                Конечно же обработчики нужно делать под конкретно требования проекта и знания разработчика. Благо способов больше одного.


                1. TheShock
                  27.12.2018 16:49

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


                  1. isden
                    27.12.2018 17:16

                    Если это ошибки, генерируемые третьесторонними или встроенными функциями — то смотреть по месту/требованиям. Иногда можно игнорить err и работать только с возвращаемым значением.
                    Если вы генерите свои ошибки, то тут уже можно сделать интересно.
                    Но в целом да, совсем от if'ов не уйти, но можно заметно сократить их количество.
                    А в других ЯП разве не так же? :)
                    Я лично вот сейчас играюсь со штуками вроде вот такой в TS:

                    type Failable<V, E> = {
                        isError: true;
                        error: E;
                    } | {
                        isError: false;
                        value: V;
                    };


                    Ничего не напоминает? :)


        1. qrKot
          27.12.2018 13:20

          Новую обработку ошибок уже везут, она уже в пути. Узбагойтесь уже)


        1. PsyHaSTe
          27.12.2018 15:36

          Вы видимо никогда не писали лапшу из and_then


          1. isden
            27.12.2018 15:41

            Писал, очень давно. «Я был молод и мне были нужны деньги» (с)
            Сейчас стараюсь избегать такого, благо с тех пор многое узнал и попробовал.


  1. johnfound
    27.12.2018 09:38
    +3

    Мне Го не нравится из за агрессивной рекламой! Я уже опасаюсь включить утюг, а то и он начнет дифирамбы петь про Го.


  1. GorodokVVV
    27.12.2018 13:05
    +1

    Да что ж вы все так убиваетесь? Вы ж так никогда не убьетесь! :)
    А если серьезно — да не пиши ты на Го, раз все так плохо. Переходи на 1С — самое оно…


  1. Harbour
    27.12.2018 13:05

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


    1. gnomeby
      27.12.2018 13:14

      Желание гугля похоронить Go, как и мнение Роб Пайка относительно умственных способностей программистов на Go, уже не важны. Языку уже условно «18 лет» и он вправе вести жизнь отдельно от родителей с их мнением по поводу будущего своего чада. А также его умственных способностей.


      1. Harbour
        29.12.2018 08:28

        возраст не имеет отношения к зрелости языка, как говорится — некоторые с возрастом становятся умнее, а некоторые просто старше. среди го тусовки наверное только Роб Пайк и еще несколько человек могут самостоятельно принимать решения о применении того или иного языка в своих проектах — остальным просто приказали или наняли по уже сформированному тз. мораль сей басни довольно проста — можно много бить себя пяткой в грудь и развешивать свое мнение на хабрах, но если гугель вдруг закроет го — то никто из этой тусовки не в состоянии будет ни финанировать, ни развивать, ни даже тупо поддерживать такой немаленький проект. более того — сверху им опять прикажут и все смирненько перейдут на питон/руби или nodejs…


        1. gnomeby
          29.12.2018 09:49

          возраст не имеет отношения к зрелости языка

          Да, конечно я имел ввиду, что язык достаточно зрел, чтобы выжить даже при закрытии.


          1. Harbour
            29.12.2018 10:20

            да, и насчет 18 лет — я бы был поскромнее. реальные проекты за пределами гугля появились всего пару лет назад. а большинство го-программистов, которых я видел были, в свое время, не в состоянии освоить c++/js/ruby. вот их и прибило к этому берегу ;)


            1. wmns
              29.12.2018 12:39

              Неосиляторы — это обратная сторона медали легкости входа.


  1. ikirin
    27.12.2018 14:02

    Go от сюдова =)
    Я правильно понимаю, что go стал популярен из-за реализации рутин-потоков?


    1. gnomeby
      27.12.2018 14:24

      Да, без этого он бы не взлетел так. Но ещё из-за:
      * низкой ресурсоёмкости
      * близости к Си
      * отсутствия головняка с компилятором и заголовочными файлами
      * отсутствия головняка со сборкой


      1. PsyHaSTe
        27.12.2018 15:37

        • агрессивной политики Гугла


        1. gnomeby
          27.12.2018 15:39

          А в чём она проявляется?


          1. PsyHaSTe
            27.12.2018 20:58

            image
            image
            (с) humbug


            1. Gizmich
              27.12.2018 21:38

              раст лэнг, серьезно? Я думаю гугл выдает наиболее популярную выдачу по lang и undef и уж извините что это Golang. Да голэнг встречается очень часто, а вот раст лэнг никто не называет, кругом только раст. Наберите просто lang undefined и получите тоже самое, ну серьезно вы же разработчик, как бы вы организовали поиск по словам с учетом актуальности и вводимых запросов?


              1. PsyHaSTe
                27.12.2018 21:47

                Ну да, самая популярная ccskrf по undef будет Go, и никак не C/C++. А lang кроме го ни один язык не называют, ни тот же С, ни какие-то еще.


                1. Gizmich
                  27.12.2018 21:52

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


                  1. TheShock
                    29.12.2018 08:56
                    +2

                    Простите, а как именно иначе гуглить Rust и C? Это ведь не уникальные названия как «JavaScript» и «PHP». У Раста даже сайт так называется:
                    www.rust-lang.org

                    У языка D — тоже: dlang.org


                    1. Gizmich
                      29.12.2018 09:02

                      rust undefined как в примере будет вполне допустимо и даже более точно если вы посмотрите выше относительно чего был мой комент. В этом запросе сознательно расширяется выдача за счет lang который выдаст более популярный похожий запрос, которым естественно называется Golang.


                      1. TheShock
                        29.12.2018 09:04
                        +2

                        Вы считаете, что в ответ на запрос «rust lang» вменяемо подставлять «go lang»?


                        1. Gizmich
                          29.12.2018 09:08
                          -1

                          да, поскольку ключевым словом здесь будет lang как наиболее часто встречающееся в запросах, полагаю что по этому сочетанию машина определит область программирования и найдет наиболее популярное сочетание программирования и слова lang и выдаст это как похожий запрос и это будет Go поскольку Golang популярен.


                          1. TheShock
                            29.12.2018 09:10

                            То есть если я гуглю «три мушкетера», то ключевым словом должно быть «три» как найболее часто встречающееся, а остальные — игнорироваться?


                            1. Gizmich
                              29.12.2018 09:14

                              Почему игнорироваться? Да главным словом будет три, но мушкетеры значительно сузит область, поисковик не выдаст вам три стула. Не пойму как вы вообще могли придти к такому выводу… Перечитайте мой комментарий еще раз.


                              1. TheShock
                                29.12.2018 09:27
                                +1

                                поисковик не выдаст вам три стула

                                Почему при запросе "три мушкетера" оно не выдаст "три стула"

                                А при запросе "lang rust" оно выдает "lang go"? Почему не произошло «сужение» области в данном случае, как должно произойти, по вашим словам?

                                Жирным я выделил то, что вы называете «главным» словом.

                                Кстати, я не думаю, что это умысел, скорее это просто неудачно сработал алгоритм. Но я не понимаю, почему вы считаете, что это нормальная работа алгоритма, а не ошибочная?


                                1. Gizmich
                                  29.12.2018 09:31

                                  Вместе с lang rust часто ищут
                                  rust язык

                                  rust ide

                                  rust lang wiki

                                  rust vs c++

                                  rust docs

                                  rust язык установка

                                  программирование на языке rust pdf

                                  rust vs go

                                  Это то что мне выдал гугл, вроде как все логично. Все еще не пойму что вас не устраивает?
                                  p.s. lang rust и lang go будет не типичным запросом и естественно что алгоритм его обработает, но не так как типичный запрос. Например если набрать«Мушкетера» то гугл первой строкой выдает мне «20 лет спустя» и «три» только 7-й позицией.


                                  1. TheShock
                                    29.12.2018 09:34

                                    Все еще не пойму что вас не устраивает?

                                    image


                        1. Edison
                          29.12.2018 11:25

                          у меня ничего не подставляет


                1. Edison
                  27.12.2018 21:53

                  у меня совсем другие результаты.

                  > А lang кроме го ни один язык не называют, ни тот же С, ни какие-то еще.

                  Зачем вы тогда rust lang написали?


                  1. PsyHaSTe
                    27.12.2018 21:55

                    Пишу так, потому что еще есть игра с тем же названием, часто в поиск попадается.


                    1. Edison
                      27.12.2018 21:57

                      гугл показывает персональные результаты, я гуглом не пользуюсь, вот сейчас попробовал — rust lang undef дополняется все с растом, undefined — C++.


                      1. humbug
                        28.12.2018 01:40

                        Да что вы говорите, прям в этом гугле обперсоналились. Я пишу на Rust 1.5 года, у меня в истории запросов раста больше, чем порева, все это время сижу залогиненный в их гугл почту, но он упорно продолжает считать меня либо го разрабом (^ выше скрины от меня), либо геймером:



                        Скриншот сделал сегодня.


                        1. Edison
                          28.12.2018 01:45

                          Вам мои скриншоты показать?


                          1. humbug
                            28.12.2018 01:47

                            Давайте. Продолжайте радовать меня идиотизмом поисковой системы, которая ваши запросы по языку Rust распознает лучше, чем мои.


                            1. Edison
                              29.12.2018 11:33

                              image


                    1. Gizmich
                      27.12.2018 22:00

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


              1. Crandel
                27.12.2018 23:45
                +2

                Есть игра Rust, постоянно попадает в выдаче, только rust lang гуглю


            1. gnomeby
              28.12.2018 07:19
              +1

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


              1. nexmean
                28.12.2018 07:42

                Вы так говорите, будто не влияет. SEO, между прочим, очень важная штука для продвижения сайта/продукта/технологии, сейчас возможно даже самая важная.


                1. gnomeby
                  28.12.2018 08:06

                  Нет не влияет. Технические решения обычно принимает CTO основываясь на своём опыте и привычках. Хотя конечно для заказной разработки, типа сайтов, может влиять.


                  1. nexmean
                    28.12.2018 09:00

                    А опыт и привычки CTO значит никак не зависят от поисковой выдачи и прочего маркетинга? Как-то слишком уж наивна мысль о том, что выбор языка программирования зависит только от каких-то объективных факторов.


        1. qrKot
          27.12.2018 16:25

          Скандалы, интриги, расследования.

          Спецназ гугла жестоко избил ногами Растеров за отказ писать на Go.

          Массовые протесты против тирании Гугла. Миллионы раст-программистов вышли на улицы, чтобы выразить свое «фи» против продвижения Go.

          Жесть! Смотреть до конца! Без регистрации и СМС.

          Минутка откровений, срыв покровов, рас-рас: «Все дело в зависти… — утверждает Rust-разработчик, пожелавший остаться неизвестным. — Мы тоже хотим агрессивно продвигать наш любимый язык, но у нас нет на это денег.» Объявлен сбор пожертвований в пользу продвижения Rust'а. Всем неравнодушным высылать деньги на электронный кошелек яндекс-денег «410013-456-88146». СРОЧНО! НЕ ДАЙ СВОЕМУ ЯЗЫКУ ПОГИБНУТЬ!


  1. tuxi
    27.12.2018 15:02

    del


  1. PsyHaSTe
    27.12.2018 15:29
    +3

    Больше всего в го расстраивает именно коммьюнити. На любую критику в сторону языка следуют какие-то религиозные мантры вместо, собственно ответа:


    • А вы вообще на го сколько писали?
    • А вот Пайку виднеее
    • А вот у меня все нормально работает
    • А зато в Го я быстрее пишу микросервис, чем на джаве
    • Зато мы не спорим про форматирование
    • А вы мне покажите язык, который защищает от всех проблем. Нет такого? Ну то-то же
    • Зато мне не надо каждый год изучать новые фичи языка
    • А ты кто такой, чтобы это спрашивать?
    • И что? Так даже удобнее
    • А кому сейчас легко?
    • ...

    И все эти "что позволено language design team, не позволено обычному ГО-разработичку" отдельно радуют.


    1. powerman
      27.12.2018 23:10

      На то есть разные причины. Я лично стараюсь такие ответы не давать, так что можете задать такие критикующие вопросы мне, если, конечно, Вам интереснее получить конкретные ответы, нежели просто продолжать критиковать ради самого процесса.


      Конкретнее по упомянутым вариантам ответов:


      • "А вот Пайку виднее" — я на авторитеты никогда не ссылаюсь, но понимаю, почему нередко отвечают именно так. Есть определённые идеи, до которых многие просто ещё не доросли. Это не понты и не взгляд свысока, это просто факт. И касается он всех, абсолютно точно включая меня, и, я полагаю, и Пайка тоже. Есть идеи, которые рождаются раньше "своего" времени, из-за чего они оказываются проигнорированы и забыты. Моё личное мнение — множество таких идей появилось лет 30 назад, в OS Plan9, потом в OS Inferno и языке Limbo. Часть этих идей, к сожалению, так и не стали популярны до сих пор, хотя процесс идёт, иногда криво реализованные, но они проникают в современные OS, ЯП и утилиты. Я лично думаю, что дело не столько в том, что эти идеи появились слишком рано, сколько в том, что их авторы абсолютно не занимались их популяризацией. Go это первый представитель части этих идей, который стал популярен (и да, произошло это именно благодаря жёсткой раскрутке его гуглом, а не благодаря его техническому совершенству — в этом плане он на старте заметно проигрывал предыдущим разработкам). Но я бы не стал винить гугл в том, что он сильно пиарит Go — тут скорее мы сами виноваты в том, что чаще всего без такого пиара не в состоянии заметить даже очень классные вещи. Так вот, извините за длинное предисловие, суть ответа "Пайку виднее" в том, что за некоторыми особенностями Go стоят именно те идеи, до понимания которых многие ещё не доросли, но идеи от этого менее правильными не становятся, поэтому иногда нужно просто делать как делают взрослые, и, со временем, станет понятно зачем и почему это делается именно так. Да, такой ответ раздражает, но учитывайте, что для того, чтобы ответить как-то более осмысленно, отвечающему самому надо сначала дорасти до этих идей, плюс уметь внятно объяснять другим то, что понял сам.
      • "Зато мне не надо каждый год изучать новые фичи языка" — это вполне валидный ответ, как по мне. Мне категорически не нравится мелькание технологий, которое сейчас происходит на фронте, а до этого происходило под виндой. Я предпочитаю использовать более стабильный стек — мне есть что непрерывно учить помимо нового синтаксиса ЯП и что делать помимо переделывания старого кода под новые требования ЯП.
      • Насчёт остальных ответов согласен.


  1. Gizmich
    27.12.2018 16:48

    Просто невероятно сколько на хабре недовольных языком Го, в каждой статье про него прямо таки битва разгорается, складывается впечатление что это ужаснейший язык на свете. Остается непонятным почему недовольные люди о нем читают, почему недовольные люди на нем пишут? Но больше всего удивляет агрессивность Расто-поклонников, они просто адски ненавидят Го, хотя казалось бы пишите на чем нравится…


    1. scalavod
      28.12.2018 00:44

      > Остается непонятным почему недовольные люди о нем читают, почему недовольные люди на нем пишут? Но больше всего удивляет агрессивность Расто-поклонников, они просто адски ненавидят Го, хотя казалось бы пишите на чем нравится…

      Я недоволен языком go. Пишу на нём потому что он используется на работе и у него есть объективные преимущества перед другими языками… и являюсь неагрессивным расто-поклонником. Кстати, по ощущениям, как-раз go-поклонники более агрессивны :P


      1. Edison
        28.12.2018 00:47

        Почитайте посты про Go на хабре.

        Я вот считаю так — если вам что-то не нравится на работе, может следует что-то поменять?


        1. scalavod
          28.12.2018 00:55
          +1

          Я разве написал что мне что-то не нравится на работе? Работой полностью доволен. А вот чем недоволен — написал в первом предложении.

          > Почитайте посты про Go на хабре
          Дайте, пожалуйста, ссылки на посты которые мне необходимо почитать.


          1. Edison
            28.12.2018 01:06

            Я разве написал, что мне что-то не нравится на работе? Работой полностью доволен. А вот чем недоволен — написал в первом предложении.

            Как я понял, программирование на Go является частью вашей работы, если вам это не нравится (программировать на Go) — то вам не нравится ваша работа. Или вам нравится программировать на Go?

            Дайте, пожалуйста, ссылки на посты которые мне необходимо почитать.

            Комменты вот тут
            habr.com/post/422049
            habr.com/post/421411


            1. PsyHaSTe
              28.12.2018 01:36

              Как я понял, программирование на Go является частью вашей работы, если вам это не нравится (программировать на Go) — то вам не нравится ваша работа. Или вам нравится программировать на Go?

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


              Я, например, пишу в последние полгода на солидити, которые в миллион раз хуже и го, и JS, и всего, что я вообще когда-либо видел. Лучше разве что брейнфака и его вариаций. Но работаю, по причине 1, 2 и 4.


              1. Edison
                28.12.2018 01:47

                Я, например, пишу в последние полгода на солидити, которые в миллион раз хуже и го, и JS, и всего, что я вообще когда-либо видел. Лучше разве что брейнфака и его вариаций. Но работаю, по причине 1, 2 и 4.

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


                1. PsyHaSTe
                  28.12.2018 01:56

                  Вы как-то очень избирательны в своем чтении. я

                  1. работаю с лучшим коллективом из 5 мест, на которых я работал
                  2. я работаю над интересными проектами, где узнал много нового
                  3. в следующем году, возможно, я буду вести проект на расте
                  4. моя компания очень лояльно относится к тому, что я часто работаю из дома
                  5.…

                  Вопрос: зачем мне увольняться? Только чтобы на солидити не писать? Есть и другие факторы, и они перевешивают. Вот и колюсь кактусом, но дописываю проект.


            1. scalavod
              28.12.2018 01:39

              Как я понял, программирование на Go является частью вашей работы, если вам это не нравится (программировать на Go) — то вам не нравится ваша работа. Или вам нравится программировать на Go?

              Я же написал что недоволен языком go, а не то что мне не нравится программировать на go.

              Если появится задача для которой go будет подходящим решением, то я спокойно буду использовать его без нравится/не нравится.


              1. Edison
                28.12.2018 01:49

                Ок, вы сломали мой мозг — вам не нравится Go, но вам нравится программировать на Go?


                1. scalavod
                  28.12.2018 02:14

                  Вы правда не видите разницу между фразами недоволен и не нравится?

                  Давайте тогда на примере, где нет go.

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

                  Так понятнее? Или вы напишете, что мне не нравится ездить на метро?

                  И если вернуться к go, то я недоволен языком go, но абсолютно спокойно отношусь к тому, чтобы что-то на нём сделать.


                  1. Edison
                    28.12.2018 02:17

                    А вы ездите на метро тоже по 8 часов в день?


    1. Edison
      28.12.2018 00:45

      Очень часто забавляет сравнение Go c С, Rust, Python, Java, Pony, C#, Haskell, Erlang, Javascript, etc одновременно. Типа:
      1. Go плохой, потому что в Rust <что-то> сделано лучше.
      2. Go плохой, потому что в Python <что-то> сделано лучше.
      3. Go плохой, потому что в Pony <что-то> сделано лучше.
      4. Go плохой, потому что в Java <что-то> сделано лучше.
      5. Go плохой, потому что в Haskell <что-то> сделано лучше.


      1. PsyHaSTe
        28.12.2018 01:39

        Да, это, конечно же, плохое сравнение.


        С другой стороны, возможно, причина в том, что Го по своим возможностям является подмножеством этих языков. Ну, за исключением совсем своеобразных вещей, вроде отсутствия зависимости на libc или там форматтер как часть компилятора. Ведь отсутствие "фич" и есть его главная фича. Suckless, так сказать.


        1. Edison
          28.12.2018 01:59

          Подмножеством чего? Максимум есть связи с C. Я когда начал писать на Go, в Rust каждый день изменяли стандартную библиотеку.


          1. PsyHaSTe
            28.12.2018 02:03

            Описанных языков. В 2009 горутины были прорывом (наверное. Я в C# пользовался тасками примерно тогда же). Сейчас же это мейнстрим в большинстве языков. Один бинарь — как уже выясняли, нужная в очень специфических условиях штука, причем скорее маркетинговая, направленная на питонистов «смотрите, интерпретатор не нужон». Что там еще есть? Быстрая компиляция? В питоне вообще мгновенная, в шарпе — сравнимая, т.к. основную работу JIT делает во время запуска, а транслировать C# в IL тривиально.

            Ну и всё, вроде. Отсутствие фич это не фича, атеизм это не религия, а лысина — не вид прически. В итоге из реально полезных вещей — удобная асинхронность, которую за 10 лет скопировали все языки/библиотеки, которые хотели, и уже не являются какой-то уникальной штуковиной.


            1. Edison
              28.12.2018 02:10

              Я так и не понял, в чем ваша претензия?


              1. PsyHaSTe
                28.12.2018 12:11

                У меня не было никакой претензии. Я говорил, что возможно "го" является подмножеством описанных языков, и объяснил, почему я так считаю.


            1. Ravager
              29.12.2018 12:04

              Сейчас же это мейнстрим в большинстве языков

              в каких?
              Один бинарь — как уже выясняли, нужная в очень специфических условиях штука, причем скорее маркетинговая, направленная на питонистов «смотрите, интерпретатор не нужон»

              лол, надо плюсовикам рассказать что они на самом деле питонисты.
              Ну и всё, вроде. Отсутствие фич это не фича, атеизм это не религия, а лысина — не вид прически.

              всегда можно придраться к чему-то. даже в вашем любимом языке может не быть фичи, которая есть в другом языке. это конечно повод накинуть какашек и собрать плюсцов себе в карму, но на реальное положение дел это не повлияет.
              Я в C# пользовался тасками примерно тогда же

              вы понимаете в чем принципиальная разница между green threads и тасками?


              1. PsyHaSTe
                29.12.2018 12:19

                в каких?

                C#/Котлин/..., вот это всё

                лол, надо плюсовикам рассказать что они на самом деле питонисты.

                У плюсовиков вообще все грустно. В 2018 году нельзя склонировать репозиторий и нажать кнопку «restore deps and build», чтобы всё заработало с первого раза. Они обрадуются вообще чему угодно :)

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

                Причем тут придирка? Если в C# нет non-null types или там тайпклассов, я не буду говорить, что это фича, потому что мне не надо учить ничего нового. Я честно скажу: да, ребята, косяк, надеюсь в будущем исправят.

                вы понимаете в чем принципиальная разница между green threads и тасками?

                Расскажите. А то я сглупу думал, что и то и то реализация M:N многопоточности.


                1. Ravager
                  29.12.2018 13:20

                  C#/Котлин/..., вот это всё

                  ну это еще не все языки. у нас например ни того ни другого нет. а есть питон, жава и го.
                  У плюсовиков вообще все грустно. В 2018 году нельзя склонировать репозиторий и нажать кнопку «restore deps and build», чтобы всё заработало с первого раза. Они обрадуются вообще чему угодно :)

                  да, поэтому го для меня был как манна небесная. я с ужасом представляю себе что нужно будет писать на плюсах проект. CMake, гора зависимостей и костыли при кросскомпиляции.
                  Причем тут придирка? Если в C# нет non-null types или там тайпклассов, я не буду говорить, что это фича, потому что мне не надо учить ничего нового. Я честно скажу: да, ребята, косяк, надеюсь в будущем исправят.

                  ну в каком-то смысле ограничения это хорошо. тот же пример — плюсы. мультипарадигменность, 100500 способов сделать одно и то же порождает разношерстный код. ограничения хороши для поддерживаемости проекта разными людьми. Но есть откровенно бесячие недостатки, с этим не поспоришь.
                  Расскажите. А то я сглупу думал, что и то и то реализация M:N многопоточности.

                  разница в том, что имея 4 потока в случае goroutine ты пишешь «синхронный» линейный IO код и не паришься, потому что под капотом планировщик и IO на самом деле асинхронный. В случае тасков если ты будешь так писать, то тебе быстро придет карачун и все зависнет


                1. qrKot
                  29.12.2018 13:23

                  C#/Котлин/..., вот это всё


                  В котлине и C# есть Green Threads? Давно? Вчера не было, например.

                  Погуглил… Гугл говорит, что все еще нет.

                  А не путаете ли вы goroutine с coroutine? Ну, как бы, корутины в Котлине есть, ага. Только вот это несколько иная конструкция.

                  А то я сглупу думал, что и то и то реализация M:N многопоточности.


                  Вы упустили одну немаловажную вещь: это разные реализации M:N многопоточности.

                  Разница в том, кто управляет многозадачностью. Корутины сами отдают управление (yield), горутины управляются планировщиком.


      1. scalavod
        28.12.2018 01:57
        +1

        Кстати, было уже How to complain about Go от divan0