Комментарии 190
А Го хорош там, для чего он создавался. А те, кто его пытаются засунуть туда, где ему не место — потом хейтят.
Где что-то из этого совпадает с Гуглом, кроме мониторингов?
не для создания API <...>, а для реализации бизнес-логики
странное противопоставление: API же не существует само по себе
Можно пример без инициализации? Т.к. инициализая все равно происходит.
Считаю, что отличие в «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 условная…
Имейте уважение к автору, который утверждает обратное:
До недавнего времени у нас не было реальных альтернатив там, где царит Go: в сфере разработки эффективных, нативных исполняемых файлов без мучений C или C++. Rust быстро развивается, и чем больше я с ним работаю, тем больше он мне кажется крайне интересным и тщательно продуманным. Я считаю, что Rust — один из тех друзей, с которыми сначала не так просто поладить, но потом хочется долго с ним общаться.
Они получили высокую производительность и небольшое потребление ресурсов памяти/процессора/диска.
Прекрасная статья. Go решает на мой взгляд очень узкую проблему как метко заметил автор статьи. У меня была задача запустить тысячу нодов кластер и опа-на! я обнаружил, что тот же ТомКет просит под сервер с апп 380М памяти, а Го скромно укладывается и на 60М. Пришлось ударными темпами переписывать под Го.
В Rust такая же проблема: поскольку в нём нет исключений (действительно нет, в отличие от Go)
Но ведь в Rust есть точно такой же panic и его так же можно поймать и отменить…
Прекрасная стандартная библиотекаЭто скорее в недостатки. Библиотека как для нового языка — непоследовательная и непродуманная. Чего только стоит совершенно разные подходы к парсингу в 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 от себя и только прокоментировал неудачные «плюсы» из топика
Какой наркоман до такого додумался — я не знаю.
Собственно сами теги.
github.com/golang/go/commit/12a3435869b17de633d50857764b9c6a055032c1
Синтаксис аля
`json:"name"`
github.com/golang/go/commit/25733a94fde4f4af4b6ac5ba01b7212a3ef0f013
Сравниим, к примеру, банальную сортировку.
Для Python всего то есть sorted, да у списков метод list.sort имеется. Весьма и весьма куцо. А вот у Go есть целый отдельный модуль sort. Тут и сортировка строк, и сортировка целых, и сортировка флоатов!
Или, например, в Go есть полезнейший пакет errors с инструментами для упрощения работы с ошибками. Он даже в отдельной директории находится, настолько он важен. В Python банально нету аналога для такого модуля.
А вот у Go есть целый отдельный модуль sort. Тут и сортировка строк, и сортировка целых, и сортировка флоатов!
Я же правильно понял, что это такая издевательская ирония?
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 и как обрабатывать ошибки.
Вы так написали, как-будто всю эту информацию нельзя сделать в голанге?)))Ну так мы то говорим в контектсе «богатство стандатной библиотеки». Из коробки таких функций нету (вообще почти ничего нету).
Откройте книжку Роба Пайка, и посмотрите пример по выводу тайминга работы функции, реализованную через defer _внезапно_ с отложенной анонимной функцией (ленивые вычисления, привет Скале). И десятом строк ниже, как пробрасывать тип error сквозь любое количество уровней с навешиванием информации на каждом уровне.Не могли бы вы снабдить ссылкими? Или цитатами. А то, знаете ли, в 21 веке я меньше всего хочу вручную отсчитывать номера страниц и строк.
Если это всё вы внимательно читали, то должны были усвоить в том числе — почему в golang нет try/except/finnaly.Моя ваша не понимать. Речь была про «testing» и его убогость, а вы переводите в сторону холивара «экепшны в Go»… Тема, безусловно, интересная, но ее обсуждение совершенно бесперспективно.
А про все эти трюки с навешиванием инфы через defer, пробросы ошибок через панику, оборачивания, дофичагачивание стектрейса (зачастую сделанного в месте логирования ошибки, а не ее генерации, лол) и прочее я в курсах… Правда, как показывает практика, зачастую обработка ошибок в Go выглядит так:
if err != nil {
return nil, fmt.Errorf("some error: %v", err)
}
PS: "(ленивые вычисления, привет Скале)" — вы это так написали, будто бы это единственный
Речь шла не про testing. Речь шла про отладку и посмертный дамп.Как раз про testing речь и шла. Перечитайте внимательнее.
Я так написал, потому что именно Скала наиболее часто упоминается как язык с функциями не имеющими побочных эффектов, что позволяет ленивые вычисления и жестокую параллельность.Вы, наверное, живете в параллельной вселенной. Ну или поисковики вам выдают статьи по скале, подстраиваясь под ваши интересы. А вообще, например, в статье на вики Scala даже не упоминается, зато есть и Haskell, и OCaml, и Scheme…
Речь шла не про testing
Прочитайте внимательно мое сообщение, я процитировал статью. Речь была о «Стандартизированный тестовый фреймворк»
Простите, но:
func Assert(cond_ bool, str_ *string){ if !cond{ panic(fmt.Sprintf("ERROR: ", str_)) } }
То есть стандартная библиотека Гоу настолько отвратительная, что даже такая функция ее украсит?
Кстати, функцию вы написали тоже отвратительную, но, видимо, евангелисты Гоу даже такое говно приводят в пример. В C# ценность AssertEquals в тестах в том, что я могу посмотреть, какое значение получилось, а не просто «получилось неправильное значение». А еще у вас на какую строку укажет эта ошибка? Там где случилась ошибка, или на библиотечную? И как на счет того, чтобы выполнить все тесты, а не упасть только на первом?
Или я не понял — это был очередной сарказм, где в виде положительного отзыва вы приводите негативный пример языка, как anjensan?
пример прекрасной функции вы не удосужились привестиЯ же привел. Читаете через слово? Или вообще половину не читаете.
Ну вкус и цвет все фломастеры квадратныеПри чем тут на вкус и цвет? Я описал, чем именно она плоха.
ну так не поленитесь: либо сами напишите, либо возьмите готовоеСперва добейся? Но зачем мне это? В мире есть множество не таких отвратительных языков, как Гоу, языков с отличной стандартной библиотекой, в том числе библиотекой тестирования. Они уже добились и убрали в этом необходимость для меня.
Вам сказали, чем плоха функция, которую вы предложили, сказали как выглядит хорошая функция, сказали где она есть. А вы в ответ — я могу за пару минут сервер с шифрованием поднять.
Это мне напоминает реакцию специалистов по PHP. Когда им указываешь, что в PHP переменные начинаются с доллара и это как-то странно, они, вместо того, чтобы сказать — да, есть косяк — начинают объяснять, что это и не проблема вовсе и вообще они сайты на раз-два делают.
Вы правда считаете, что вариант, который я предложил за 10 секунд — это итоговое решение?
Не знаю. Обсуждаем то, что вы представили.
Или если в стандартную библиотеку не входит какая-то плюшечка, которую вы так любите (при этом задать вопрос Яндексу лень) — это сразу плохой язык?))
Нет, но если в библиотеку не входит какая-то плюшечка, которая мне нужна, то библиотека хуже, чем библиотека, в которой эта плюшечка есть.
Я так полагаю, что обсуждение всегда начинается, чтобы найти решение проблемы (если она есть)
В данном случае обсуждение началось потому, что вопреки тому, что пишет автор, стандартная библиотека для тестирования в Go явно плохая. Если бы автор не говорил, что она превосходна, никто бы ему ничего не сказал.
Ну и чтобы найти решение проблемы, надо для начала признать её существование.
Понятно. Не знаю, но обсуждаем.
Вообще логично, что я никак не могу знать, считаете ли вы решение, которое вы предложили за 10 секунд итоговым. Но вы почему-то удивляетесь, что я, не зная, считаете ли вы решение итоговым, обсуждаю его с вами. Это очень странно.
Ну, так если всё хорошо, что вы делаете в этой ветке? ))
Ну вообще я хотел сказать вам, что вы игнорируете что вам пишет TheShock.
Вы там что, курите? В каком месте было написано «превосходная библиотека» в статье?
Да, автор говорил не про превосходную библиотеку, а про прекрасный фреймворк. Судя по вашем комментарию, вы считаете, что это коренным образом всё меняет.
Почему я должен брать в расчёт то, что мне не интересно или ошибочно сформулировано изначально?
Потому, что вы участвуете в диалоге, и, если вас не интересует, что говорит собеседник — что вы вообще делаете в этой ветке? :)
потому что я хоть библиотеку не смотрел, ничего не искалПочему не смотрел? Я вполне себе писал на 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{}) (надеюсь не надо пояснять зачем); но я допуская что вы, как истинный
5. в коде тупейшая ошибка! вместо «ERROR: » нужно писать «ERROR: %s».
Как по мне, так это вполне себе «отвратительный код».
И компилятор за это по рукам не даст? А что случится, рантайм-ошибка, креш или что-то ещё?
Не даст, будет очень странная строка. Вот плейграунд:
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
На всякий случай я показал, что это действительно формируется такая строка, а не просто ошибка в консоль пишется и как результат — может улететь в базу в виде запроса или еще что.
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) этой проблемы уже не будет. Причём не просто не будет, а всё будет работать лучше, чем сейчас в других языках!
Если честно, я тоже считал это проблемойА как быть с рефакторингом? Как IDE узнает нодо изменить имя метода или нет если в интерфейсе мы поменяли?
Аргументация? :)
Потому что вы сейчас пишете не на асемблере, и причина сложность, вы предпочитаете что-то более простое и удобное в работе.Ну так ведь наоборот, высокоуровневые языки сложнее. Не путайте пожалуйста сложность самого языка (количество фич в нем, абстракций и т.п.) и сложность написания кода на этом языке. Так-то 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 новых полей, по которым тоже надо сортировать…
func (a, b interface{})
, но сделать приличный репортинг дифа технически вполне можно. Без дифа даже из-коробки имеется reflect.DeepEqual. Я Go плохо знаю. interface{}
в данном случае должен быть интерфейсом, поля которых мы сравниваем? Если да, то проверит ли он в compile time что a и b реализуют этот интерфейс, причем для a
он является наиболее специфичным?
object
.В том-то и дело, что пример который я дал один раз строит по типам лямбду, а потом просто её вызывает с нулевым оверхедомА в чем профит если не секрет? Дабы юнит тесты выполнялись на пару ms быстрее? :)
Go вариант больше похож на старый-добрый рефлекшн.Это и есть старый добрый рефлекшн.
А в чем профит если не секрет? Дабы юнит тесты выполнялись на пару ms быстрее? :)
Ну, отчасти для образовательных целей, отчасти да, чтобы было быстрее. Т.к. эта функция вызывалась по сути в нескольких сотнях тестов для проверки тысяч объектов в каждом. Экономия даже пары десятков секунд на выполнение тестов на ровном месте это уже неплохо, как мне кажется.
а вы сравнивали тайминги? Я в тестах reflect.DeepEqual, точнее уже готовой обертки типа testify.assert, чем это отличается от вашего?
вы сравниваете С# реализации, а не с Go reflect.DeepEqual
. Код нужно скомпилировать, заранить тесты. Насколько существенная разница будет для 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 работает.
А простота нужна когда приходят новые люди в проект, особенно если они новички. Погружение в код быстрее происходит. (имхо)
Форма объявления с присвоением в голанге — целых ТРИ!
Кто третья?
Но на сколько iota реально востребована?
Вполне востребована и выполняет свою функцию: уменьшает количество ошибок, которые были бы при ручном объявлении перечислений.
Но встретить просто return без возвращаемого значения в голанге…
Да, на мой взгляд это лишняя фича.
В голанге нужно явно получать адрес переменной при передаче по ссылке.
Далеко не во всех случаях, в большинстве случаев компилятор это делает сам, а где не делает — на то обычно есть причина.
В голанге понятие пакета сделано странно.
Экспорт в голанге большой буквы, и сокрытие с маленькой — ну это какая-то фигня.
Вкусовщина.
Их только две: var
и :=
. Вторая внутри for
(а так же if
и switch
) ничем не отличается от использования вне этих конструкций.
это будет много ошибок?
Прилично. Когда выражение нужно ручками задавать для каждой переменной, да ещё и для каждой разное, то ошибок будет явно больше, чем если его задать один раз для всех сразу.
Забылся, шифт не отжал и получи косяк.
Чушь. Если шифт не нажал где нужно — не будет доступа снаружи к нужному идентификатору, это невозможно не заметить поскольку это помешает реализовать функционал, ради которого идентификатор задумывался экспортируемым. Если шифт нажал где ненужно (гораздо более частый случай) — линтер потребует написать документацию, потому что это обязательно для публичного интерфейса, так что эта ошибка проживёт до первого запуска линтера (т.е. до ближайшего коммита). И, в конце концов, звёздочку в конце имени можно ровно так же забыть или добавить лишнюю.
Была заявка на модульность, а получилось как-то не очень.
Вообще-то нет. Заявку на модульность делают только сейчас, в vgo. До этого заявка была только на пакеты, т.е. по сути на namespace — это нужно для модульности, но это не она.
Их только две: var и :=. Вторая внутри for (а так же if и switch) ничем не отличается от использования вне этих конструкций.А как вы относитесь к '
for ... := range ... {
'. Это отдельная форма присвоения, это вообще не форма присвоения, это частный случай ":="?Это не частный случай, а абсолютно обычный. Вы же не считаете a := f()
частным случаем a := 5
?
a := "abc"
Тип abc
— string
, тип a
— тоже string
. for a := range "abc"
Тут переменная a
имеет тип int
.for _, a := range "abc"
А вот тут тип уже int32
(т.е. rune
).А в чём отличия-то? range от строки возвращает не строку, а индекс символа и сам символ. Вы создаёте новую переменную с тем же именем и присваиваете туда возвращённое range значение.
Оператор :=
работает здесь ровно так же, как и в любом другом месте:
- требует чтобы слева была хоть одна новая переменная,
- затеняет существующую переменную в новой области видимости при необходимости,
- создаёт новые переменные,
- присваивает значения всем заданным переменным.
Если присмотреться чуть внимательнее, то можно заметить, что это вообще не отдельная операция ":=", а часть синтаксиса for-each.
В спеке он, кстати, описывается в отдельной секции синтаксиса циклов, а не в секции оператора :=.
И как часто она используется?
не часто, но удобная когда нужно сгенерить разные последовательности.
Можно же генерить с любым шагом или, например, n^2 последовательности
В голанге нужно явно получать адрес переменной при передаче по ссылке.
в 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?
- Будут скопированы из b2 в объект b?
- Или в переменную b попадёт значение из переменной b2 и таким образом b станет указывать на объект b2?
Если переменные передаются по значению, то у нас должен работать первый вариант, если по ссылке, то второй. Какой из вариантов происходит в Go?
Я уже устал объяснять — передается указатель по значению, потом перезаписывается объект, на который указывает указатель.
Код: https://play.golang.org/p/pcc6SG8cvP0
Передаются не объекты, а переменные. Передаются они в джаве только по значению.
насколько я понял, в Java переменные типа Object obj = new Object()
являются ссылочными переменными.
Если в go нет ссылок, то как же происходит РАЗЫМЕНОВАНИЕ??))
РАЗЫМЕНОВАНИЕ чего?
В Go нету ссылок, есть указатели.
Даже в документации Go часто находят слово "reference", есть issue на гитхабе на счет этого. Но это не означает, что в Go есть передача по ссылке или ссылочные переменные.
речь идёт не про ссылки и указатели, а про ссылки и указатели.
Что?
Что я не понял: фактически в примере на С++ создаются три переменных, указывающих на одну и туже переменную.
Правильно, две переменные ссылаются на первую — адрес у всех одинаковый. Эти две переменные называются ссылками (ссылочными переменными).
В Go такого нету. Можно создать переменную, которая будет хранит адрес на участок памяти где хранится другая переменная (например, переменная varFoo
). Но это будет новая переменная, она будет хранится отдельно в памяти и иметь свой адрес. Это называется указатель.
Этот указатель можно скопировать (значение его будет все то же, то есть будет хранить все тот же адрес переменной varFoo
). Указатель можно разыменовать и получить новую переменную со значение переменной varFoo
.
Это ссылочные переменные. Там же есть код, скопируйте и запустите у себя. Адрес всех трех переменных будет один и тот же. Потому что b и c это ссылки на a.
В Go такое не возможно.
Точно так же, при передаче по ссылке, адрес переменной в вызываемой функции будет тот же, что и в вызывающей.
В Go такое не возможно.
Там не ссылки в Go, там указатели.
Ссылочная переменная это алиас на другую переменную. Такого в Go нету.
Скопируйте/скомпилируйте/запустите код, что там показан и проанализируйте результат.
Там одна переменная и две ссылки.
Давайте я сделаю это за вас:
#> 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
Ну а ссылки, что? Разве не переменные?
Это ссылочные переменные.
Я спрашиваю про другое: вот эти три ссылки — они В ОДНОЙ ЯЧЕЙКЕ памяти лежат?
Там одна переменная (которая храниться в памяти) и две ссылочные переменные (что как бы не совсем переменнае). Вот вам почитать:
https://isocpp.org/wiki/faq/references#overview-refs
Но тут другое дело, такого в Go нету и вы ошибались.
В голанге нет передаче по ссылке?
Нету, в вашем примере идет передача указателя по значению. 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. При заворачивании типа в интерфейс тоже «под капотом» хранится указатель, что тоже по сути может приводить к «передаче по ссылке».
Мне вот просто любопытно — зачем Вы подчёркивания используете?
Go: Хороший, плохой, злой