Как стать автором
Обновить

Комментарии 190

Люди зачем-то пытаются писать вне Гугла на языке, который Гугл придумал исключительно сам для себя. И удивляются, что получается плохо.
Ангулар был создан гуглом для гугла. Как по мне, когда он вышел, избавил многих от головной боли в написании одностраничных приложений и получалось хорошо.
А Го хорош там, для чего он создавался. А те, кто его пытаются засунуть туда, где ему не место — потом хейтят.
за что заминусовали интересно? за ангулар?
Тут сидят молодые разработчики на Реакте, которым сказали на гурсах гикбрейнса, что Реакт — совершенство, а Ангулар — зло. Теперь они ходят по комментариям и минусуют все записи, где фигурирует Ангулар. Ну-ка товарищи, которые минусуют, скажите альтернативу первому Ангулар, когда он только вышел?
НЛО прилетело и опубликовало эту надпись здесь
Потому что неплохо так получается. Да и разве в языке дело, а не в инженерных задачах и подходах к их решению?
Неплохо так получается в случаях, когда Ваши задачи и подходы в их решении случайно совпадают с гугловскими. Это делает 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 от себя и только прокоментировал неудачные «плюсы» из топика
Какой наркоман до такого додумался — я не знаю.

Собственно сами теги.
github.com/golang/go/commit/12a3435869b17de633d50857764b9c6a055032c1

Синтаксис аля `json:"name"`
github.com/golang/go/commit/25733a94fde4f4af4b6ac5ba01b7212a3ef0f013
Ради деталей можно еще глянуть на ревью по ссылке из второго коммита golang.org/cl/4645069
Вот зря вы так про стандартную библиотеку. Она очень мощная.

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

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

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

Т.е. «не имеющий аналогов» «полезнейший пакет errors» не смутило? :)
Кстати, «из коробки» начиная с 1.8 можно ведь сортировать и slice-ы! А чтобы это было быстро (рефлексия ведь), добавили специальную функцию reflect.Swapper. Так что библиотека не только мощная, но еще и гибкая!
НЛО прилетело и опубликовало эту надпись здесь
Всякие 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 и как обрабатывать ошибки.
Ну может быть вы нас просветите. А то мы люди темные, читали невнимательно.
НЛО прилетело и опубликовало эту надпись здесь
Вы так написали, как-будто всю эту информацию нельзя сделать в голанге?)))
Ну так мы то говорим в контектсе «богатство стандатной библиотеки». Из коробки таких функций нету (вообще почти ничего нету).

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

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

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


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

Речь шла не про 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 поставляется с прекрасным тестовым фреймворком в стандартной библиотеке». Мне не нравится маркетологическая лодь, потому я ее опроверг, в т.ч. на примере.
потому что я хоть библиотеку не смотрел, ничего не искал
Почему не смотрел? Я вполне себе писал на 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».

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

Не даст, будет очень странная строка. Вот плейграунд:
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 узнает нодо изменить имя метода или нет если в интерфейсе мы поменяли?
НЛО прилетело и опубликовало эту надпись здесь
Если поменяли в интерфейсе, то менять надо однозначно
А если Я нигде не передаю эту структуру там где ожидают этот интерфейс?
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Аргументация? :)

НЛО прилетело и опубликовало эту надпись здесь
Главная фича этого языка — простота, и он отлично конкурирует скажем с питоном. Вообще язык отличный. А про PR от гугла как-то надуманно, зачем? Это же не коммерческий проект типа Java. Вам гугл не предъявит за использование языка в вашем проекте, как оракл.
НЛО прилетело и опубликовало эту надпись здесь
Потому что вы сейчас пишете не на асемблере, и причина сложность, вы предпочитаете что-то более простое и удобное в работе. 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 он является наиболее специфичным?

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

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

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

а вы сравнивали тайминги? Я в тестах reflect.DeepEqual, точнее уже готовой обертки типа testify.assert, чем это отличается от вашего?

Сравнивал. Например получить свойство через рефлекшн в 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 работает.

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

Кто третья?


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

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


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

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


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

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


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

Вкусовщина.

НЛО прилетело и опубликовало эту надпись здесь

Их только две: 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

НЛО прилетело и опубликовало эту надпись здесь
Если в go нет ссылок, то как же происходит РАЗЫМЕНОВАНИЕ??))

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


В 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 нету и вы ошибались.

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь
В голанге нет передаче по ссылке?

Нету, в вашем примере идет передача указателя по значению. 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. При заворачивании типа в интерфейс тоже «под капотом» хранится указатель, что тоже по сути может приводить к «передаче по ссылке».

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий