Pull to refresh

Comments 190

Люди зачем-то пытаются писать вне Гугла на языке, который Гугл придумал исключительно сам для себя. И удивляются, что получается плохо.
Ангулар был создан гуглом для гугла. Как по мне, когда он вышел, избавил многих от головной боли в написании одностраничных приложений и получалось хорошо.
А Го хорош там, для чего он создавался. А те, кто его пытаются засунуть туда, где ему не место — потом хейтят.
за что заминусовали интересно? за ангулар?
Тут сидят молодые разработчики на Реакте, которым сказали на гурсах гикбрейнса, что Реакт — совершенство, а Ангулар — зло. Теперь они ходят по комментариям и минусуют все записи, где фигурирует Ангулар. Ну-ка товарищи, которые минусуют, скажите альтернативу первому Ангулар, когда он только вышел?
UFO just landed and posted this here
Потому что неплохо так получается. Да и разве в языке дело, а не в инженерных задачах и подходах к их решению?
Неплохо так получается в случаях, когда Ваши задачи и подходы в их решении случайно совпадают с гугловскими. Это делает Go похожим на какую-то библиотеку, а не на язык программирования общего назначения.
Написано: сервис массового импорта товаров, обработки изображений под highload, мобильный back для мессенджера, протокол обмена сообщениями в Ethereum, куски самого Ethereum, всякие мониторинги до кучи.
Где что-то из этого совпадает с Гуглом, кроме мониторингов?
Думаете у гугла нет сервисов массового импорта, обработки изображений или бекендов мессенджеров? Именно для такого го и делался. Мне вот было бы интересно посмотреть на его применение в чём-то десктопном (с UI), в системном программировании, в мобильной разработке. Но таких кейсов нет, всё сводится к «принять 2 байта по сети, отправить 2 байта по сети».
Повторюсь, ну вот я сейчас в мобильной разработке пишу back. То есть гошка крутится к клиента на телефоне и подключается к фронту.
не для создания API <...>, а для реализации бизнес-логики

странное противопоставление: API же не существует само по себе
Не совсем. Есть узкий спектр задач, когда необходимо реализовать внешнее API как обертку над существующим сервисом. С развязкой через tcp/rest/etc. При этом логики в таких обвязках довольно таки мало. Конвертация параметров, конвертация представления данных в запросе, etc.
> Главное отличие в том, что var позволяет объявлять без инициализации (потом приходится объявлять тип)
Можно пример без инициализации? Т.к. инициализая все равно происходит.
Считаю, что отличие в «non-declaration statement outside function body».
Нельзя написать a := «test» вне функции.

Всё несколько глубже. Вот более-менее полный список нюансов про := и var.


Факты:


  • явно указать тип можно только в var
  • наглядно сгруппировать (в скобках, с выравниванием) несколько переменных можно только в var
  • создать глобальные переменные может только var
  • создать переменные локальные для if, for, switch может только :=
  • задать смесь новых и существующих можно только в :=
    • использование := в этом случае избавляет от лишних строк объявляющих
      переменные да ещё и с обязательным указанием типа (что не всегда
      возможно — в переменной может хранится значение не экспортируемого
      типа возвращаемое некоторыми конструкторами)

Выводы:


  • var более функционален для объявления новых переменных
  • var более нагляден для объявления новых переменных
  • var защитит от случайного изменения существующих переменных вместо объявления новых (go vet -shadow, go-nyet и т.п. могут детектить shadowing, что может снять претензию к := — а плюс в том, что не нужно заранее объявлять и указывать тип)

И пара цитат из книжки Кернигана:


  • var name type или var := value в зависимости от важности инициализации начальным значением.
  • if err := ...; err != nil {} для уменьшения области видимости err.
странно, когда в языке, вроде как с строгой типизацией, дают возможность делать много неявных вещей…

и казалось бы, язык прост, но в ходе написания реального кода для production, простым вещам требуется много уделять внимания, что позволяет выстрелить в ногу, теми же интерфейсами, а ресурсоемкую рефлексию использовать не хочется… а про надстройки работы с типами в go мне ничего не известно…

добавили бы уже опцию компилятора\макрос к функции: если string пытаешься свести к int и panic — возвращать 0, один фиг явность в go условная…

не сильно понял пример, в чем проблема там? Интерфейсная переменная содержит тип и указатель на значение. Конечно же когда пытаетесь сделать type assertion интерфейсной переменной в другой тип, а не в тот тип, который переменная содержит, получаете панику.

Да давно уже понятно, что Go это тупо хайп, который уже сейчас начал спадать. Практически все кто писал на серьезных языках типа С#/Java от Go просто чертыхаются. Ибо он реально ущербный какой-то.
Ну я, например, долгое вермя писал(и пишу) на C#/Java и мне Go нравится.
Спасибо, качественная статья, по совокупности пока не вижу ничего лучше Go а различные подводные камни можно тем или иным образом обходить.

Имейте уважение к автору, который утверждает обратное:


До недавнего времени у нас не было реальных альтернатив там, где царит Go: в сфере разработки эффективных, нативных исполняемых файлов без мучений C или C++. Rust быстро развивается, и чем больше я с ним работаю, тем больше он мне кажется крайне интересным и тщательно продуманным. Я считаю, что Rust — один из тех друзей, с которыми сначала не так просто поладить, но потом хочется долго с ним общаться.
Прямо видно как комьюнити сильно разделилось на тех кто пишет на Go, но жалуется на его недостатки и тех кто хвалит Rust за его эффективность, но фактически язык не востребован на рынке. С моей точки зрения Rust конечно эффективнее и лучше задуман, но читать код на нем мне просто больно. И это как борьба асемблера с Си, Си менее эффективен, но он удобнее. п.с. может Go 2.0 спасет нас всех? ))

Я за 3 дня нашел работу на Rust. Рынок бурлит!

Они получили высокую производительность и небольшое потребление ресурсов памяти/процессора/диска.


Прекрасная статья. Go решает на мой взгляд очень узкую проблему как метко заметил автор статьи. У меня была задача запустить тысячу нодов кластер и опа-на! я обнаружил, что тот же ТомКет просит под сервер с апп 380М памяти, а Го скромно укладывается и на 60М. Пришлось ударными темпами переписывать под Го.

TomCat просит 380, но сколько просил бы, скажем, Netty?
В Rust такая же проблема: поскольку в нём нет исключений (действительно нет, в отличие от Go)

Но ведь в Rust есть точно такой же panic и его так же можно поймать и отменить…

Подскажи как отменить панику, если в настройках стоит panic = abort?

Никак, но это и не поведение по-умолчанию.
Я-то в курсе, что panic в rust это совсем не то, чем кажется, просто в статье слишком безаппеляционно сказано, что исключения в Go есть, а в Rust нет.
Автор так старался поддерживаться нейтралитета, что записал два недостатка в преимущества.

Прекрасная стандартная библиотека
Это скорее в недостатки. Библиотека как для нового языка — непоследовательная и непродуманная. Чего только стоит совершенно разные подходы к парсингу в flag (через ссылочную муть с тонной копипасты и никакой декларативностью) и json/xml (через теги). При это flag — совершенно не критична к производительности, ведь парсинг запускается лишь однажды!

Стандартизированный тестовый фреймворк
А это в «Ужасный». Серьезно, фреймворк настолько отвратительный, что лучше его бы не было. В нем просто никаких преимуществ, даже банального Assert нету, а из-за отсутствия Generic написать свой, адекватный — крайне сложно. То есть банальный Assert из C# в Go выглядит так (в синтаксисе мог ошибиться, т.к. давно не писал):
a := fn()
if a != 10 {
  t.Fail(fmt.Sprintf("Expect a to be (%v), actual: (%v)", 10, a);
}


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


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


Тестовый фреймворк отличный. И свою задачу он выполнил — отсутствие assert-ов стимулировало попробовать писать тесты в табличном стиле, и помогло оценить этот подход. А потом assert-ы элементарно добавляются поверх, например: https://godoc.org/github.com/powerman/check

К несчастью для нас — её писали люди
Можете привести похожие примеры в стандартной библиотеке, к примеру, C#?

тесты в табличном стиле
Которые, кстати, тоже вручную пишутся. Класс. Так зачем такая надобиблиотека? И вы называете её классной просто потому что из-за неудобства вам приходится искать способ занять позу поудобнее? Очень похоже на Стокгольмский Синдром: «насильник был хороший, потому что я сам мог выбрать позу»
Плюс help для каждого флага занимает много места, и внутри тегов это бы смотрелось не очень
Ну а это вы уже совершенно уродский дизайн тегов в языке осуждаете. Какой наркоман до такого додумался — я не знаю. Во всех адекватных языках аналоги позволяют писать текст любой длины в анотациях без всяких проблем:
[ObsoleteAttribute("This property is obsolete. Use NewProperty instead.", false)]
public static string OldProperty

Я просто уже не стал добавлять в Ugly от себя и только прокоментировал неудачные «плюсы» из топика
Вот зря вы так про стандартную библиотеку. Она очень мощная.

Сравниим, к примеру, банальную сортировку.
Для Python всего то есть sorted, да у списков метод list.sort имеется. Весьма и весьма куцо. А вот у Go есть целый отдельный модуль sort. Тут и сортировка строк, и сортировка целых, и сортировка флоатов!

Или, например, в Go есть полезнейший пакет errors с инструментами для упрощения работы с ошибками. Он даже в отдельной директории находится, настолько он важен. В Python банально нету аналога для такого модуля.
А вот у Go есть целый отдельный модуль sort. Тут и сортировка строк, и сортировка целых, и сортировка флоатов!

Я же правильно понял, что это такая издевательская ирония?

Т.е. «не имеющий аналогов» «полезнейший пакет errors» не смутило? :)
Кстати, «из коробки» начиная с 1.8 можно ведь сортировать и slice-ы! А чтобы это было быстро (рефлексия ведь), добавили специальную функцию reflect.Swapper. Так что библиотека не только мощная, но еще и гибкая!
UFO just landed and posted this here
Всякие Assert нужны вовсе не для того, чтобы тупо кинуть панику, а чтобы напечатать детальное сообщение при фейле теста (мол ожидали то и то, получили это). И не просто fmt.Sprintf("expected %v, actual %v", expected, actual), а удобочинаемый дифф, чтобы потом не ломать себе глаза, высматривая «ну и где же тут, !»№%, отличие...". Хорошие сообщения о фейлах очень и очень помогают при анализе ошибок на всяких CI.

Для сравнения посмотрите (я даже не прошу внимательно читать, просто глянуть одним глазком), как это сделали, например, в Python.
type-specific equality function will be called in order to generate a more useful default error message
А если у вас свои структуры данных, то в фреймворк можно добавить хук для своего типа.

если ещё внимательно почитаете про recover и гарантированный defer…
Также вы, похоже, всё же не понимаете методику проведения проверок на допустимость значений в golang и как обрабатывать ошибки.
Ну может быть вы нас просветите. А то мы люди темные, читали невнимательно.
UFO just landed and posted this here
Вы так написали, как-будто всю эту информацию нельзя сделать в голанге?)))
Ну так мы то говорим в контектсе «богатство стандатной библиотеки». Из коробки таких функций нету (вообще почти ничего нету).

Откройте книжку Роба Пайка, и посмотрите пример по выводу тайминга работы функции, реализованную через defer _внезапно_ с отложенной анонимной функцией (ленивые вычисления, привет Скале). И десятом строк ниже, как пробрасывать тип error сквозь любое количество уровней с навешиванием информации на каждом уровне.
Не могли бы вы снабдить ссылкими? Или цитатами. А то, знаете ли, в 21 веке я меньше всего хочу вручную отсчитывать номера страниц и строк.

Если это всё вы внимательно читали, то должны были усвоить в том числе — почему в golang нет try/except/finnaly.
Моя ваша не понимать. Речь была про «testing» и его убогость, а вы переводите в сторону холивара «экепшны в Go»… Тема, безусловно, интересная, но ее обсуждение совершенно бесперспективно.

А про все эти трюки с навешиванием инфы через defer, пробросы ошибок через панику, оборачивания, дофичагачивание стектрейса (зачастую сделанного в месте логирования ошибки, а не ее генерации, лол) и прочее я в курсах… Правда, как показывает практика, зачастую обработка ошибок в Go выглядит так:
if err != nil {
	return nil, fmt.Errorf("some error: %v", err)
}


PS: "(ленивые вычисления, привет Скале)" — вы это так написали, будто бы это единственный известный вам язык, где есть ленивые вычисления…
UFO just landed and posted this here
Изначально вы отвечали на коммент TheShock, где он писал дескать «это ложь, что библиотека в Go хорошая; например вот модуль testing просто ужасен».

Речь шла не про testing. Речь шла про отладку и посмертный дамп.
Как раз про testing речь и шла. Перечитайте внимательнее.

Я так написал, потому что именно Скала наиболее часто упоминается как язык с функциями не имеющими побочных эффектов, что позволяет ленивые вычисления и жестокую параллельность.
Вы, наверное, живете в параллельной вселенной. Ну или поисковики вам выдают статьи по скале, подстраиваясь под ваши интересы. А вообще, например, в статье на вики Scala даже не упоминается, зато есть и Haskell, и OCaml, и Scheme…
Речь шла не про testing

Прочитайте внимательно мое сообщение, я процитировал статью. Речь была о «Стандартизированный тестовый фреймворк»
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Простите, но:
func Assert(cond_ bool, str_ *string){
    if !cond{
        panic(fmt.Sprintf("ERROR: ", str_))
    }
}

То есть стандартная библиотека Гоу настолько отвратительная, что даже такая функция ее украсит?
Кстати, функцию вы написали тоже отвратительную, но, видимо, евангелисты Гоу даже такое говно приводят в пример. В C# ценность AssertEquals в тестах в том, что я могу посмотреть, какое значение получилось, а не просто «получилось неправильное значение». А еще у вас на какую строку укажет эта ошибка? Там где случилась ошибка, или на библиотечную? И как на счет того, чтобы выполнить все тесты, а не упасть только на первом?

Или я не понял — это был очередной сарказм, где в виде положительного отзыва вы приводите негативный пример языка, как anjensan?
UFO just landed and posted this here
пример прекрасной функции вы не удосужились привести
Я же привел. Читаете через слово? Или вообще половину не читаете.

Ну вкус и цвет все фломастеры квадратные
При чем тут на вкус и цвет? Я описал, чем именно она плоха.

ну так не поленитесь: либо сами напишите, либо возьмите готовое
Сперва добейся? Но зачем мне это? В мире есть множество не таких отвратительных языков, как Гоу, языков с отличной стандартной библиотекой, в том числе библиотекой тестирования. Они уже добились и убрали в этом необходимость для меня.
UFO just landed and posted this here

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


Это мне напоминает реакцию специалистов по PHP. Когда им указываешь, что в PHP переменные начинаются с доллара и это как-то странно, они, вместо того, чтобы сказать — да, есть косяк — начинают объяснять, что это и не проблема вовсе и вообще они сайты на раз-два делают.

UFO just landed and posted this here
Вы правда считаете, что вариант, который я предложил за 10 секунд — это итоговое решение?

Не знаю. Обсуждаем то, что вы представили.


Или если в стандартную библиотеку не входит какая-то плюшечка, которую вы так любите (при этом задать вопрос Яндексу лень) — это сразу плохой язык?))

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


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

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


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

UFO just landed and posted this here
Понятно. Не знаю, но обсуждаем.

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


Ну, так если всё хорошо, что вы делаете в этой ветке? ))

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


Вы там что, курите? В каком месте было написано «превосходная библиотека» в статье?

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

UFO just landed and posted this here
Почему я должен брать в расчёт то, что мне не интересно или ошибочно сформулировано изначально?

Потому, что вы участвуете в диалоге, и, если вас не интересует, что говорит собеседник — что вы вообще делаете в этой ветке? :)

UFO just landed and posted this here
Зачем он написал то, что написал?

В статье была ложь о, цитирую: «Go поставляется с прекрасным тестовым фреймворком в стандартной библиотеке». Мне не нравится маркетологическая лодь, потому я ее опроверг, в т.ч. на примере.
потому что я хоть библиотеку не смотрел, ничего не искал
Почему не смотрел? Я вполне себе писал на Go, так что имею мнение прям с передовой, в отличии от вас:
1. Я не гофер. И даже не программист.

Тестовая либа в языке — отвратительная, приходится искать сторонние решения. Процитирую вам статью, которую вы, видимо, читали через строчку, раз пропустили эту фразу:
Go поставляется с прекрасным тестовым фреймворком в стандартной библиотеке
Я спорил именно с этим утверждением. Тестовая либа/фреймворк в Гоу — отвратительная, а автор утверждает, что она прекрасная. Нету в ней ничего прекрасного, приходится пользоваться или сторонними решениями, или писать глючные костыли типа вашего.
Простите, но:
func Assert(cond_ bool, str_ *string){
    if !cond{
        panic(fmt.Sprintf("ERROR: ", str_))
    }
}
1. Я не считаю эту функцию отвратительной и пример прекрасной функции вы не удосужились привести.
Ну это уже на цирк какой то смахивает. Вот вам по пунктикам, в порядке моего офигевания:
1. code-style — что за 'cond_', зачем 'str_'; не надо так;
2. code formatting — куда делись пробелы между ) и {; go fix такой код не пропустит;
3. а нафига, простите, тут '*string', вместо 'string'; вопрос риторический, можете не отвечать…
4. придирочка — гораздо лучше сделать 'Assert(cond bool, fmt strging, a ...interface{}) (надеюсь не надо пояснять зачем); но я допуская что вы, как истинный сусликгофер, хотите сделать две функции Assert и Assertf, допустим;
5. в коде тупейшая ошибка! вместо «ERROR: » нужно писать «ERROR: %s».

Как по мне, так это вполне себе «отвратительный код».
UFO just landed and posted this here
В чём проблема то? Я так и не понял))
1. Я не гофер. И даже не программист.
При этом активно спорите, что-то доказываете (частенько дичь всякую).
В этом, собственно, и проблема :)
UFO just landed and posted this here
UFO just landed and posted this here
И компилятор за это по рукам не даст? А что случится, рантайм-ошибка, креш или что-то ещё?

Не даст, будет очень странная строка. Вот плейграунд:
fmt.Println("Hello, playground") // Hello, playground
	
str_ := "123";
fmt.Println(fmt.Sprintf("ERROR: ", str_)); // ERROR: %!(EXTRA string=123)
fmt.Println(fmt.Sprintf("ERROR: %s", str_)); // ERROR: 123
	
result := fmt.Sprintf("ERROR: ", str_)
fmt.Println(result) // ERROR: %!(EXTRA string=123)
fmt.Println(len(result)) // 27


На всякий случай я показал, что это действительно формируется такая строка, а не просто ошибка в консоль пишется и как результат — может улететь в базу в виде запроса или еще что.
UFO just landed and posted this here
3. Вам никто не запрещает посмотреть что получилось. Делается дописыванием параметров.

Кстати, это интересно было бы посмотреть. Можно пример?
Здесь применяется мантра «Не взаимодействую с помощью общей памяти, делай память общей с помощью взаимодействия».

Этот принцип не подходит для одной из самой важной части бэкенда — базы данных или разного рода кешей. Когда есть большая структура-граф объектов в оперативной памяти то нет никакого смысла хранить копию этой структуры в разных горутинах или потоках не говоря уже о необходимости синхронизации частичных обновлений этой структуры между ними и опасности race condition. Для таких задач нужна именно общая память и параллельный доступ к памяти из разных потоков

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

Структура хранится в одной горутине — выделенном «менеджере» этой структуры. А все остальные горутины получают доступ к ней общаясь по каналам с горутиной-менеджером

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

Если эта структура — god object, который хранит все данные приложения — то да. Не надо так делать. А если таких структур куча (и у каждой своя горутина-менеджер) — то нет, не превратится в однопоточное.


Насчёт RWMutex Вы правы, он позволит распараллелить доступ. Но обычный Mutex точно так же выстроит всех в одну очередь, как и горутина-менеджер.

В статье «Less is exponentially more» Роб Пайк почему-то относит обобщённые типы и наследование к «типизированному программированию» и говорит, что предпочитает композицию, а не наследование.

Наследование нужно для того чтобы оптимизировать логику декораторов которые в случае композиции будут создавать отдельные объекты в рантайме. Допустим у нас есть класс DBConnection объект которого представляет собой соединение с базой данных. Применяя композицию обычно создают отдельный класс Repository который представляет собой crud-операции с таблицами базы данных, который в конструкторе создает new DBConnection(...) и использует его для взаимодействия c базой данных. А вот применяя наследование вместо композиции класс Repository отнаследуется от DBConnection и добавит нужный код работы с crud. И здесь принципиальное отличие — в случае композиции при создании объекта Repository будет создано два объекта в рантайме (сам Repository и объект DBConnection) а применяя наследование — только один объект. А в случае если у на будет цепочка из 10 различных сущностей которые что-то добавляют и переопределяют то с композицией это уже 10 рантайм-объектов а с наследованием только один вне зависимости от длины этой цепочки (да хоть тысячу сущностей). В это и суть наследования — оно позволяет вынести в compile-time много работы экономя cpu-циклы и память

Все Ваши проблемы синтетические. Как меня задолбал Хабр подобными постами.

В общем и целом статья отличная! Немного комментариев:


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

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


Go игнорирует достижения современного проектирования языков

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


Трудно понять, какие типы реализуют конкретный интерфейс

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


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

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


Синтаксис := позволяет случайно «затенить» переменную.

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


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

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


Кошмар управления зависимостями

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

UFO just landed and posted this here
Если честно, я тоже считал это проблемой
А как быть с рефакторингом? Как IDE узнает нодо изменить имя метода или нет если в интерфейсе мы поменяли?
UFO just landed and posted this here
Если поменяли в интерфейсе, то менять надо однозначно
А если Я нигде не передаю эту структуру там где ожидают этот интерфейс?
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Главная фича этого языка — простота, и он отлично конкурирует скажем с питоном. Вообще язык отличный. А про PR от гугла как-то надуманно, зачем? Это же не коммерческий проект типа Java. Вам гугл не предъявит за использование языка в вашем проекте, как оракл.
UFO just landed and posted this here
Потому что вы сейчас пишете не на асемблере, и причина сложность, вы предпочитаете что-то более простое и удобное в работе. Go такой же простой в использовании как питон, но производительнее, вот почему он де факто на рынке конкурирует с питоном, там где раньше использовали бы питон (тот же веб, сервисы) сейчас пишут на Go. При сопоставимой простоте/сложности вы получаете бонусом производительность.
Потому что вы сейчас пишете не на асемблере, и причина сложность, вы предпочитаете что-то более простое и удобное в работе.
Ну так ведь наоборот, высокоуровневые языки сложнее. Не путайте пожалуйста сложность самого языка (количество фич в нем, абстракций и т.п.) и сложность написания кода на этом языке. Так-то Brainfuck вообще архипростой, всего 8 символов, 1 тип данных… Красота, а не язык!

Go такой же простой в использовании как питон

Ох… ну раз уж я тут в другой ветке поднял тему сортировок… давайте сравним еще раз. Пример из статьи:
оimport "sort"

type Person struct {
    Name string
    Age  int
}

// ByAge implements sort.Interface for []Person based on the Age field.
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func SortPeople(people []Person) {
    sort.Sort(ByAge(people))
}

И вот аналогичный код на Python (даже с тайпхинтами!, без них еще проще):
import typing

class Person(typing.NamedTuple):
    name: str
    age: int

def SortPeople(people: typing.List[Person]):
    people.sort(key=lambda p: p.age)

А теперь представим, что понадобилось сортировать еще и по Name (в зависимости от выбора пользователя)… А потом добавилось еще 5 новых полей, по которым тоже надо сортировать…
А с удобной compile-time рефлекшном можно и такое забацать, а потом писать
AssertEx.AllPropertiesAreEquals(expected, actual)

Даже если авторы компилятора забыли это написать. Ах да, пользователи языка — смерды, и не имеют права на написание обобщений…
Ну справедливости ради такое можно и в Go замутить. Сигнатурка будет не ахти func (a, b interface{}), но сделать приличный репортинг дифа технически вполне можно. Без дифа даже из-коробки имеется reflect.DeepEqual.

Я Go плохо знаю. interface{} в данном случае должен быть интерфейсом, поля которых мы сравниваем? Если да, то проверит ли он в compile time что a и b реализуют этот интерфейс, причем для a он является наиболее специфичным?

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

Go вариант больше похож на старый-добрый рефлекшн.
Это и есть старый добрый рефлекшн.
А в чем профит если не секрет? Дабы юнит тесты выполнялись на пару ms быстрее? :)

Ну, отчасти для образовательных целей, отчасти да, чтобы было быстрее. Т.к. эта функция вызывалась по сути в нескольких сотнях тестов для проверки тысяч объектов в каждом. Экономия даже пары десятков секунд на выполнение тестов на ровном месте это уже неплохо, как мне кажется.
Сравнивал. Например получить свойство через рефлекшн в 1000 раз дольше, чем получить его напрямую (линк). Если у нас несколько сотен тестов (возьмем 250), в каждом 1000 объектов, у каждого из которых 5 свойств, которые мы сравниваем парами (еще х2), то это 2500000 вызовов. Используя данные по ссылке, это займет 0.53 секунды при прямом вызове, и 482 секунды (8 минут) через рефлекшн. Как по мне, разница существенная, даже если тесты гоняются параллельно.

вы сравниваете С# реализации, а не с Go reflect.DeepEqual. Код нужно скомпилировать, заранить тесты. Насколько существенная разница будет для Go?

Ну я не проверял, но думаю, что вряд ли Go каким-то магическим образом сильно отличается, поэтому можно принять данные по шарпу с точностью до порядка. Если вы считаете иначе, то прошу дать ссылку на сравнение, потому что у меня после беглого гугления информации не нашлось.
Можно подумать в Go рефлексия молниеносно быстрая.
Вот например баг. Там указывают вполне реалистичные оценки
reflect.Value.Call is ~65-80x slower than other invocation types


Как я понимаю PsyHaSTe не призывал так делать для Go (для и шарпа походу больше для just-for-fun сделал, что есть респект). В Go такое банально не сделаешь — ну нету в нем рантаймовой генерации байткода / компиляции на лету…

если это реально узкое место, то можно и код генерить. Мне интересно сравнить всю цепочку

Я Go плохо знаю.

но достаточно, чтобы критиковать?

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

import "sort"

type Person struct {
	Name string
	Age  int
}

func SortPeople(people []Person) {
	sort.Slice(people, func(i, j) bool {
		return people[i].Age < people[j].Age
	})
}


и разница внезапно становится не столь значительной.
Если уж вас слайсы интересуют вот код:
import "sort"

type Person struct {
	Name string
	Age  int
}

func SortPeople(pp []Person) {
	sort.Slice(pp, func(i, j int) bool { return pp[i].Age < pp[j].Age })
}


А вообще, если вас интересует выразительность, то да — python более выразительный. На нем можно меньше печатать. Но у меня, например, большая часть времени не набор кода уходит, а на его проектирование. Так go еще и быстрее python работает.

А простота нужна когда приходят новые люди в проект, особенно если они новички. Погружение в код быстрее происходит. (имхо)
UFO just landed and posted this here
Хотелось бы чтобы место go занял d. Но без бабок и pr это сейчас или невозможно или практически невозможно.
Я думаю что хорошим пиаром для языка будут сильные проекты на нем. Сейчас очень хайповый и популярный проект Докер, он сам по себе и пиарит Go
Pr докера это отдельная тема. На d есть прекрасный проект vibe.d брал типы в разных бенчмарках. И тем не менее все ещё никому не нужен
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Go подкупает простотой и минимализмом, он просто вынуждает всех писать простой, понятный, линейный код без всяких извратов в стиле «смотри как я могу!». После ентерпрайзных проектов на C#, с огромной complexity и купой абстракций, Go стал прям глотком свежего воздуха. Но потом приходит осознание того что разруха в головах, и на том же привычном C# можно писать простой и понятный код. Потом попробовал в деле .NET Core (кроссплатформенный, сильно похудевший, переработанный .NET для тех кто не знает), и знаете, разработка очень похожа на Go, только с мощностью C# без компромиссов и нормальным тулингом. Единственная проблема это командная разработка — если ты проникся простым и понятным кодом и можешь себя контролировать не использовать фичи C# не к месту, коллеги часто такое воротят что глаза на лоб лезут. На GO им делать это было бы сложнее.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Форма объявления с присвоением в голанге — целых ТРИ!

Кто третья?


Но на сколько iota реально востребована?

Вполне востребована и выполняет свою функцию: уменьшает количество ошибок, которые были бы при ручном объявлении перечислений.


Но встретить просто return без возвращаемого значения в голанге…

Да, на мой взгляд это лишняя фича.


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

Далеко не во всех случаях, в большинстве случаев компилятор это делает сам, а где не делает — на то обычно есть причина.


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

Вкусовщина.

UFO just landed and posted this here

Их только две: var и :=. Вторая внутри for (а так же if и switch) ничем не отличается от использования вне этих конструкций.


это будет много ошибок?

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


Забылся, шифт не отжал и получи косяк.

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


Была заявка на модульность, а получилось как-то не очень.

Вообще-то нет. Заявку на модульность делают только сейчас, в vgo. До этого заявка была только на пакеты, т.е. по сути на namespace — это нужно для модульности, но это не она.

Их только две: var и :=. Вторая внутри for (а так же if и switch) ничем не отличается от использования вне этих конструкций.
А как вы относитесь к 'for ... := range ... {'. Это отдельная форма присвоения, это вообще не форма присвоения, это частный случай ":="?

Это не частный случай, а абсолютно обычный. Вы же не считаете a := f() частным случаем a := 5?

На самом деле отличия есть, вот смотрите.

a := "abc"
Тип abcstring, тип a — тоже string.

for a := range "abc"
Тут переменная a имеет тип int.

for _, a := range "abc"
А вот тут тип уже int32 (т.е. rune).

А в чём отличия-то? range от строки возвращает не строку, а индекс символа и сам символ. Вы создаёте новую переменную с тем же именем и присваиваете туда возвращённое range значение.


Оператор := работает здесь ровно так же, как и в любом другом месте:


  • требует чтобы слева была хоть одна новая переменная,
  • затеняет существующую переменную в новой области видимости при необходимости,
  • создаёт новые переменные,
  • присваивает значения всем заданным переменным.
Вы не можете использовать ":= range" вне цикла.
Если присмотреться чуть внимательнее, то можно заметить, что это вообще не отдельная операция ":=", а часть синтаксиса for-each.

В спеке он, кстати, описывается в отдельной секции синтаксиса циклов, а не в секции оператора :=.

Всё верно, но мне казалось мы тут обсуждаем количество разных вариантов объявления переменных — 2 их или 3. Их 2. А range это часть for и к этой теме вообще отношения не имеет.

И как часто она используется?

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

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

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

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

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

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

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

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

нет, есть передача по ссылке, есть передача по значению (не важно, что передается). Некоторые языки имеют и передачу по ссылке и по значению. Ссылка это не указатель, это два разных понятия. В Go же есть только передача по значению.

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

нет, в Java, насколько я знаю, объекты передаются по ссылке. Можно ли в Java сделать, например, вот так в Go:


package main

import (
    "fmt"
)

type bar struct {
    k string
    v int
}

func main() {
    b := bar{k: "1", v: 1}
    foo(&b)
    fmt.Print(b)
}

func foo(b *bar) {
    b2 := bar{k: "2", v: 2}
    *b = b2
}
нет, в Java, насколько я знаю, объекты передаются по ссылке

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


Можно ли в Java сделать, например, вот так в Go:

Если в b в main напечатается k: 2 и v: 2, то нельзя. Плюс это означает передачу параметра b по ссылке.

Передаются не объекты, а переменные.

Простите, что????


Передаются они в джаве только по значению.

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


Object obj = new Object()

Тут obj хранит ссылку или сам объект (значение)?


Если в b в main напечатается k: 2 и v: 2, то нельзя. Плюс это означает передачу параметра b по ссылке.

fmt.Print(b) выведет k: 2 и v: 2. Это не ссылка, это указатель передали в другую функцию по значению. Указатель содержит адрес, это просто значение тип 0x...... Если скопировать это значение у другую переменную — тогда она тоже будет указывать на тот же объект.
Тип в функции func foo(b *bar)*bar указатель, причем типизированный указатель. Есть еще unsafe.Pointer.

Тут obj хранит ссылку или сам объект (значение)?

В переменной obj — указатель на объект.


fmt.Print(b) выведет k: 2 и v: 2.

Каким образом двойки попадут в b?


  1. Будут скопированы из b2 в объект b?
  2. Или в переменную b попадёт значение из переменной b2 и таким образом b станет указывать на объект b2?

Если переменные передаются по значению, то у нас должен работать первый вариант, если по ссылке, то второй. Какой из вариантов происходит в Go?

Я уже устал объяснять — передается указатель по значению, потом перезаписывается объект, на который указывает указатель.
Код: https://play.golang.org/p/pcc6SG8cvP0

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

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

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

насколько я понял, в Java переменные типа Object obj = new Object() являются ссылочными переменными.

В терминологии C и C++ переменные типа Object в Java — указатели.

в С++ есть точно такие же референс переменные

Нет, точно такие же в С++ — указатели. Вот совсем точно такие же, один в один. Аналога референс переменных из C++ в Джаве нет.

окей, доверюсь вам, я не настолько хорошо знаю что там в java, с первого взгляда было похоже, что как раз С++ ссылки в java

UFO just landed and posted this here
Если в go нет ссылок, то как же происходит РАЗЫМЕНОВАНИЕ??))

РАЗЫМЕНОВАНИЕ чего?


В Go нету ссылок, есть указатели.

UFO just landed and posted this here
UFO just landed and posted this here
речь идёт не про ссылки и указатели, а про ссылки и указатели.

Что?


Что я не понял: фактически в примере на С++ создаются три переменных, указывающих на одну и туже переменную.

Правильно, две переменные ссылаются на первую — адрес у всех одинаковый. Эти две переменные называются ссылками (ссылочными переменными).


В Go такого нету. Можно создать переменную, которая будет хранит адрес на участок памяти где хранится другая переменная (например, переменная varFoo). Но это будет новая переменная, она будет хранится отдельно в памяти и иметь свой адрес. Это называется указатель.


Этот указатель можно скопировать (значение его будет все то же, то есть будет хранить все тот же адрес переменной varFoo). Указатель можно разыменовать и получить новую переменную со значение переменной varFoo.

UFO just landed and posted this here

Это ссылочные переменные. Там же есть код, скопируйте и запустите у себя. Адрес всех трех переменных будет один и тот же. Потому что b и c это ссылки на a.
В Go такое не возможно.


Точно так же, при передаче по ссылке, адрес переменной в вызываемой функции будет тот же, что и в вызывающей.
В Go такое не возможно.

UFO just landed and posted this here

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

UFO just landed and posted this here

Там одна переменная и две ссылки.
Давайте я сделаю это за вас:


#> test  % cat main.cpp                                                                                                                                                             
#include <stdio.h>

int main() {
        int a = 10;
        int &b = a;
        int &c = b;

        printf("%p %p %p\n", &a, &b, &c);
        return 0;
}
#> test  % gcc main.cpp -o main                                                                                                                                                     
#> test  % ./main                                                                                                                                                                   
0x7fff53584858 0x7fff53584858 0x7fff53584858
UFO just landed and posted this here
Ну а ссылки, что? Разве не переменные?

Это ссылочные переменные.


Я спрашиваю про другое: вот эти три ссылки — они В ОДНОЙ ЯЧЕЙКЕ памяти лежат?

Там одна переменная (которая храниться в памяти) и две ссылочные переменные (что как бы не совсем переменнае). Вот вам почитать:
https://isocpp.org/wiki/faq/references#overview-refs


Но тут другое дело, такого в Go нету и вы ошибались.

UFO just landed and posted this here

В Go нету ссылок.
b = &a;b тут указатель, а не ссылка.

UFO just landed and posted this here
В голанге нет передаче по ссылке?

Нету, в вашем примере идет передача указателя по значению. self *TDive — это указатель. Содержит (хранит в памяти) адрес указывающий на объект. Это просто значение. Оно хранится в памяти, потому можно даже получить указатель на эту переменную (указатель на указатель).
Разберитесь, что такое ссылка и что указатель, и почитайте что в Go.

Я, конечно, люблю Go, и люблю называть вещи своими именами, даже очень. И я читал http://spf13.com/post/go-pointers-vs-references/. Но, по-моему, Вы всё-таки перегибаете палку. Если разработчик писал на плюсах, где есть и то и другое, то он, предположительно, разбирается в отличиях и не запутается с указателями в Go. Всем остальным — пофигу. У меня до Go в опыте разные языки, от ассемблера до перла, но плюсов нет, и ни в одном из моих языков ссылок как в плюсах не было — зато во многих были указатели, где с адресной арифметикой, где без. И называли их в половине случаев — ссылками. По настроению, в основном. Могли в одном и том же предложении одну и ту же переменную назвать и ссылкой и указателем. И никогда это не вызывало проблем с пониманием. (Более того, в перле, кстати, что-то подобное ссылкам плюсов таки есть — только называют их алиасами, и реализованы они не столько в самом языке, сколько в отдельной библиотеке.) В общем, называть вещи своими именами важно, но здесь и сейчас Вы — перегибаете.

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

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

Ссылка и указатель это не одно и то же.
map это структура (как и слайс) которая содержит указатель, потому такое и поведение.

Спасибо, я в курсе как устроен map. Но, ещё раз попытаюсь объяснить, правильные термины важны не сами по себе, а потому что они проясняют ситуацию. В нашем случае терминологически правильное заявление "map передаётся по значению" ничего не проясняет, а наоборот, запутывает. Разница между передачей по значению и по ссылке/указателя важна не потому, что описывает внутреннее устройство языка, а потому что показывает программисту чего ему ожидать — сможет ли вызванная функция изменить переданное ей значение, или нет. А когда термины вроде правильные, только ни разу не помогают понять что происходит — они бесполезны. А как только они становятся бесполезны — все становится плевать, каким словом что "правильно" называть.

Да, все всегда передается по значения. Это правда, но вот в значении может быть неявный указатель:
1. Встроенные типы (slice, map, chan) хранят внутри себя указатели. Так что, например, map-ы семантически передаются таки «по ссылке»;
2. При заворачивании типа в интерфейс тоже «под капотом» хранится указатель, что тоже по сути может приводить к «передаче по ссылке».

Мне вот просто любопытно — зачем Вы подчёркивания используете?

UFO just landed and posted this here

Линтеры не используете, или затыкаете? А то некоторые на подчёркивания конкретно ругаются… и shadowing они, вообще-то, ловят на раз, так что особо напрягаться смысла мало.

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Sign up to leave a comment.