Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Критики Go любят замечать, что «в Go нет ничего нового», но они упускают то, что это является хорошим качеством для языка программирования.

да еще и ни в одном серьезном проекте не засветился.Facebook, Youtube, сервисы Яндекса для начала. Список можно расширить ещё большим количеством примеров и вообще дополнить китайскими проектами. Т.е. написанное вами неверно вроде той степени, в какой неверно моё утверждение про 4 страницы A4.
Facebook, к примеру, предложил эффективную реализацию джинериков для Go
Note: This was a joke made for Gophercon2015. Sorry if you came here looking for something useful.


Такие вещи надо, как минимум, обосновывать. А еще лучше доказывать.
char *p = new char[size](); // Две круглые скобочки превращают malloc в calloc!calloc'а.new явно обнуляет массив и тем самым делает его «грязным», calloc же ипользует mmap «под капотом», что для больших массивов заметно эффективнее.Он объёмный (много фич), но понятный.«Много фич» — это и значит «непонятный».
using в классе. И даже несмотря на то, что такое использование прямо разрешено в style guide код пришлось переписывать, чтобы другие разработчики от него не офигели. На больших проектах под сложность языка всегда подразумевается именно сложность чтения и понимания чужого кода. $ cat test.cc
#include <iostream>
#ifndef IN_CLASS_DECLARATION
#define IN_CLASS_DECLARATION 1
#endif
#define FOO_FUNCTION \
void foo(const cls<T> &a, T b) { \
std::cout << "foo(T):" << a.t << " " << b << std::endl; \
}
template<typename T>
class cls {
public:
cls(T t_) {
t = t_;
}
operator T() {
return t;
}
#if IN_CLASS_DECLARATION
friend FOO_FUNCTION
#endif
T t;
};
#if !IN_CLASS_DECLARATION
template<typename T> FOO_FUNCTION
#endif
void foo(double, double) {
std::cout << "oops" << std::endl;
}
int main() {
cls<double> a = 1;
foo(a, 1);
}
$ g++ -DIN_CLASS_DECLARATION=1 test.cc -o test-in
$ g++ -DIN_CLASS_DECLARATION=0 test.cc -o test-out
$ ./test-in
foo(T):1 1
$ ./test-out
oops
foo, это был operator, который, понятно, взаимодействовал с кучей других operatorов из других включаемых файлов.T *p=new T();" не стоит комментарий, что скобочки тут не просто так, а по делу, то кто-нибудь может их и убрать — и компилятор ему слова дурного не скажет, а падать ваша программа начнёт самым странным и непредсказуемым способом.new, но в один прекрасный момент вам это потребуется (ну, скажем, чтобы два процесса могли общаться через shared-memory) и вот тут-то у вас проблем возникнет — мало не покажется.… не хочу изучать все эти Go, Rust, D и ещё полтора десятка… Меня устраивает язык, которым я сейчас пользуюсь 95% времени, потому что считаю его лучшим из существующих на данный момент.
Чем больше библиотек, тем проще найти нужную и прикрутить в своё приложение вместо того, чтобы тратить время на имплементацию велосипеда, который уже писали тысячи человек до того… на других языках. Я хочу писать уникальную функциональность, а не очередную обёртку над threading API / библиотку для работы с UTF-8 строками / ещё какую-то фигню, которая сжирает кучу времени и без которой нельзя нормально реализовать высокоуровневую логику
Более того, я надеюсь, что эти языки не взлетят и тихо загнутся.
«Много фич» — это и значит «непонятный».
Мешанина значков, которые никак не могу запомнить как работают.
<- = стрелочка «данные идут справа налево»[] = это квадратные скобочкиitem := <- myChannel
myChannel <- item
Я достаточно давно работаю с С++, и никогда не имел проблем с С++
То есть можно ли на Go писать реальные сервера, которые будут удовлетворять стандартам.
Я не представляю, на какой другой технологии может быть реализовано подобное.ProtoBuf и его развитие Cap'n Proto отлично справятся. Правда в C++. Как оно там в Go — не знаю.
encoding/xml.Меня одного напрягает обилие восхваляющих Go статей, без единой строчки кода?
Как в Go решена проблема структурной типизации вида: есть два различных интерфейса с похожими с точностью до сигнатуры методами, как реализовать оба интерфейса, если реализации их должны быть различны?
interface XMLSerializable {
serialize() : String
}
interface JSONSerializable {
serialize() : String
}
class Human {
serialize() {
return "human name=Jim"
}
}
function createJSONResponse( obj : JSONSerializable ) {
return new JSONResponse( obj.serialize() )
}
createJSONResponse( new Human )
Подобные статьи общего плана можно написать и про brainfuck. Без конкретных примеров, где были бы видны преимущества, такие маркетинговые статьи «для привлечения внимания» ничего не стоят.
type Human struct {
Name string `json:"name"`
}
h := Human{Name:"Jim"}
data, err := json.Marshal(h)
// MarshalJSON implements Marshaller for Human
func (h Human) MarshalJSON() ([]byte, error) {
return []byte(`{"human name": "` + h.Name + `"}`), nil
}
type Human struct {
Name string `json:"human name", xml:"HumanName"`
}
Постарайтесь абстрагироваться от конкретных интерфейсов, каждый из которых может быть описан в отдельном модуле и вообще не знать о существовании друг друга.
Two interface types are identical if they have the same set of methods with the same names and identical function types. Lower-case method names from different packages are always different. The order of the methods is irrelevant.
type XMLSerializer interface {
serialize()
}
type JSONSerializer interface {
serialize()
}
Это два одинаковых интерфейса.
Структурно же они идентичны, что вводит компилятор в заблуждение об их эквивалентности.
XMLSerializer::serialize(), в другом — совсем другая (хотя, может быть, и похожая) функция JSONSerializer::serialize(). У них одинаковые входные/выходные аргументы и даже часть названия, но это разные функции. Если вы будете в своей документации ссылаться на обе как на serialize(), то никто ничего не поймёт.// Интерфейс человеков
type Human interface {
Run()
}
// Интерфейс просессов операционной системы
type Process interface {
Run()
}
// Класс весёлых персонажей
type Person struct {
Name string
}
func (c *Person) Run() {
fmt.Println("Hello, my name is ", c.Name , " and i am running")
}
// Класс разрушительных процессов
type WorldDestroyer struct {
Name string
}
func (c *WorldDestroyer) Run() {
Destroy( &World{Name: c.Name} )
}
// функция для игры с весёлыми персонажами
func playInFunnyGames(h1 Human, h2 Human) {
h1.Run()
h2.Run()
fmt.Println("Innocent game complete")
}
// Создали первого персонажа
p1 := &Person{Name: "Jim"}
// Ой, где-то ошиблись и в переменной оказался объект совсем не того класса
p2 := &WorldDestroyer{Name: "Lucky"}
// Молодёжная комедия превращается в фильм-катастрофу
playInFunnyGames( p1 , p2 )
// Интерфейс человеков
type Human interface {
Run()
}
Здесь вы написали буквально следующее: человек — это такая хрень, которая умеет бегать. То есть лошадь — это у нас человек, процесс — это тоже человек, и вообще — куча вещей, которые явно людьми не являются — всё человеки. Вам не кажется это… хмм… несколько странным?
// Ой, где-то ошиблись и в переменной оказался объект совсем не того класса
p2 := &WorldDestroyer{Name: "Lucky"}
Оказывается проблема у нас не в том, что у нас интерфейс Human неотличим от интерфейса Process, а в том, что ошибки определённого класса у нас не отлавливаются. Ну да. А кто сказал, что язык обязан такие ошибки отлавливать? На практике эта проблема «случайно» не возникает.Ну да ладно. Пусть у нас человек — это тот, кто бегает. И лошадь тоже. И процесс. Ну вот такой у нас ограниченный мир.Интересно, как вы без помощи номинативной типизации сможете отличить человека от, например, обезьяны или андроида. Дайте догадаюсь, введёте уникальный для каждого вида метод типа такого:
type Human interface {
iAmHuman()
Run()
}
type Monkey interface {
iAmMonkey()
Run()
}
Оказывается проблема у нас не в том, что у нас интерфейс Human неотличим от интерфейса Process, а в том, что ошибки определённого класса у нас не отлавливаются. Ну да.И не отлавливаются они потому, что эти два интерфейса не отличимы. Наконец-то вы признали эту проблему :-) Теперь о следующей:
На практике эта проблема «случайно» не возникает.Вы случайно не из тех легендарных программистов, кто никогда не допускает ошибок и рядом с которым другие программисты тоже перестают допускать ошибки? :-)
Так стоит ли переживать из-за того, что в Go у вас механизм, который решал 1% (ну 5%, от силы — 10%) проблемы перестал действовать?Перила пригождаются гораздо реже 1% случаев, но когда пригождаются ты понимаешь, что стоят они тут не зря.
Зато он же и перестал требовать костылей в случаях, которые у вас в «нормальных» языках разруливаются с большим трудом.
class StorableShape : Shape { // наследуемся от фигуры
private DBObject _store; // агрегируем совсем другой объект
alias _store this; // становимся подтипом этого другого объекта
this() {
_store = new DBObject;
}
...
}
Интересно, как вы без помощи номинативной типизации сможете отличить человека от, например, обезьяны или андроида.А зачем вам отличать обезьяну от андроида? Поймите вы: если у вас возникла проблема «как отличить обезьяну от человека», значит вы что-то сделали не так. Если в вашем мире нет никаких действий, которые может исполнять андроид, но не может исполнять обезьяна, то вам их и отличать не нужно, вот и всё. Если есть — то они автоматически станут отличимы и будут отличаться там и тогда, где это нужно.
Теперь о следующей:О ужас! О качмар! О боги, это ж никак нельзя разрулить. А, кстати, как вы разрулите эту проблему в Java? Если у вас первая библиотека будет требовать, чтобы класс реализовывал интерфейс AlphaNumerable, а вторая DbNumerable — оба с методами GetId? И кто помешает это же решение применить в Go?
1. мы подключаем две библиотеки из ортогональных областей
2. мы создаём свой кдласс, объекты которого хотелось бы использовать и с одной и с другой библиотекой
3. первая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать строку удовлетворяющую некоторым ограничениям: он должен состоять исключительно из цифробуквенных символов
4. вторая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать валидный идентификатор в БД вида "#\d+:\d+"
5. первый идентификатор можно получить из второго не сложными преобразованиями над строками (base32 например)
6. как в одном объекте реализовать оба варианта getId, чтобы один и тот же объект можно было использовать с обеими библиотеками?
Вы глубоко ошибаетесь полагая, что «правильное именование методов» каким-либо образом спасёт вас от данной проблемы.А почему нет?
В Java эта проблема не разруливаетсяА тогда что мы вообще тут сейчас обсуждаем? Если эта проблема не мешает создавать «ынтырпрайзных» монстров на миллионы строк на Java, то почему она помешает их создавать на Go? А если не создавать программы при которых ограничение на 2GiB кода в одном бинарнике становится проблемой, то и вообще проблем не будет.
поэтому там принято давать многословные имена, во избежание возможных конфликтов :-)То есть всё-таки проблема разруливается?
Значит ли это что все проблемы, на которые так любят пинать любители Go, настолько несущественны, что ими стоит пренебречь, Go нужно закопать и всем взяться за изучение свежего сборника граблей С++15?Во-первых C++15 в природе нет и не будет. Вы уж определитесь — вы про C++14 или C++17 говорите. А во-вторых самая большая проблема C++ (как уже обсуждалось), которую призван решить Go — это его сложность. О чём, в частности, написано в обсуждаемой нами статье (или вы уже забыли). Единственная способ борьбы с ней — это умение говорить «нет». Соответственно любые проблемы в Go обсуждаются на в разрезе «если долго-долго высасывать проблему из пальца, то можно насосать вот это», а «мы X раз сталкивались с проблемой Y, решали её так-то и так-то, а теперь предлагаем всё-таки немного усложить язык, чтобы с ней больше не пришлось сталкиваться».
Потому что невозможно синхронизовать работу тысяч программистов, чтобы они не использовали одни и те же имена для разных сущностей в своих библиотеках.Почему нельзя? У других как-то получаются. Да, бывают коллизии, ну так нужно связаться с разработчиками, обсудить их, разрулить, в крайнем случае. А в большинстве случаев, если у людей мозги есть, проблем не будет. Как-то же люди на C пишут, а там вообще плоское пространство глобальных имён. Так можно дойти до того, чтобы вообще запретить людям имена придумывать, а заставлять их использовать рандомно сгененированные последовательности букв и цифр. А то как бы чего не вышло. Единственная реальна проблема, которую несёт в себе подход Go — это возможные коллизии имён. Тут вы правильно заметили. Да, это [потенциальная] проблема, но на практике коллизии случаются достаточно редко для того, чтобы вокруг этой проблемы не нужно было выстраивать огород — вот и всё.
Вы сейчас с JS программистом разговариваете. О каких «знакомых мне классах» вы говорите? ;-)О всё тех же самых. Ваши мозги также безнадёжно искалечены Smalltalk'ом, как и мозги Java-разработчиков и C++-разработчиков.
При этом вы вынуждены публичным методам объектов давать глобально уникальные имена.И вот тут это прекрасно видно. То, что у разработчиков Smalltalk'а случилось помутнение сознания и они засунули методы в объекты не означает, что так и должно быть. Подавляющее большинство методов объектам не принадлежат! Метод «изобрази себя» логически не принадлежит на объекту «круг», ни объекту «квадрат», ни объекту «экран», ни объекту «принтер» — он связывает между собой эти сущности. И именно за счёт этого, как правило, между разными методами не возникает коллизий даже с короткими именами.
MarshalJSON/MarshalXML) у вас возникает связь не с двумя входящими объектами, а с входящим объектов и выходящим, а так как сигнатура выходящего в типе не участвует то возникает естественное желание внести её в имя метода). Поймите же вы, наконец, что эти методы названы так, как они названы не для того, чтобы «развести» потенциальные коллиззии, а чтобы облегчить чтение программы. Если у вас есть интерфейс, который включает в себя метод getId, который должен генерировать алфивитно-цифровой Id, то его название плохо не из-за того, что могут коллизии возникнуть, а из-за того, что такое название подразумевает, что вам годится любая строка в качестве Id, что есть неправда.Если у вас есть интерфейс, который включает в себя метод getId, который должен генерировать алфивитно-цифровой Id, то его название плохо не из-за того, что могут коллизии возникнуть, а из-за того, что такое название подразумевает, что вам годится любая строка в качестве Id, что есть неправда.Как же его правильно назвать? GetIdWithAlphaNumericalLettersAndUnderscoreOnly? :-)
type MyHuman interface {
Human
MyRun()
}
func (c *Person) MyRun() {
c.Run()
}
// ну и соответственно в playInFunnyGames использовать не Human, а MyHuman
func playInFunnyGames(h1 MyHuman, h2 MyHuman) {
h1.Run()
h2.Run()
fmt.Println("Innocent game complete")
}
// для человеков просто создаём песочницу и пусть бегают в ней
func RunInSandbox(h Human) {
var sb = &SandBox{}
sb.Put( h )
h.Run()
}
// для процессов временно создаём изолированный контекст, который не влияет на текущий
func RunInSandbox(h Process) {
IsolateContext()
defer DestroyContext()
h.Run()
}
// создаём уничтожитель миров
w := &WorldDestroyer{Name: "Lucky"}
// какая реализация будет вызвана?
runInSandbox( w )
какая реализация будет вызвана?

if err != nil {
return err
}
Эволюция Go