Почему Go — это плохо продуманный язык программирования

https://medium.com/@tucnak/1cc04e5daf2
  • Перевод
Это перевод статьи юзернейма tucnak с Medium, которая получила обширное обсуждение на reddit.com/r/programming.

image
Окей, заголовок действительно несколько громкий, признаю. Cкажу больше: я прусь от громких заголовков, все из-за внимания. В этой блогозаписи я постараюсь доказать тот факт, что Go это ужасно продуманный язык (спойлер: это так). Я уже играюсь с Go уже на протяжении нескольких месяцев, первый helloworld собрал, кажется, в июне. Математик из меня никакой, но с тех пор прошло уже что-то около 4 месяцев и я даже успел залить на Github несколько репозиториев и собрать немного звезд! Стоит также упомянуть, что у меня совершенно нет опыта применения Go в продакшне, так что любые мои слова о «поддержке кода» или «деплое» не стоит принимать за единственноверную истину.

Я люблю Go, я полюбил его как только впервые попробовал его. Я потратил несколько дней на то, чтобы принять идиоматику, смириться с отсутствием дженериков, разобраться с откровенно странным способом обработки ошибок и вы знаете, всеми этими классическими проблемами, так или иначе связанными с Go. Я прочел Effective Go, много статеек из блога Dave Cheney, следил за всеми новостями из мира Go. Я даже могу сказать, что я достаточно активный участник сообщетсва! Я люблю Go и ничего не могу с этим поделать — Go просто замечательный. Тем не менее, я считаю, что Go это ужасный плохо продуманный язык, который делает совершенно не то, что «продает».

Go считается простым языком программирования. Как сказал Rob Pike, они «убрали из языка все, что только можно было убрать», что сделало его спецификацию просто тривиальной. Эта сторона языка просто удивительна: ты можешь выучить основы в течении минут, сразу начать писать реальный код и и в большинстве случаев Go будет себя вести ровно так, как ты ожидаешь. Ты будешь много беситься, но к счастью, все будет работать круто. В реальности все немного иначе, Go это далеко не простой язык, скорее просто плохой. А теперь взгляните на несколько аргументов, которые подтверждают мои слова.

Причина №1. Манипуляции со слайсами просто отвратительны

Cлайсы (это такие навороченные массивы) очень классные, мне безумно нравится концепт и непосредственно реализация. Но давайте на одну секундочку представим, что нам какой-то момент захочет написать с ними немного исходного кода, может быть совсем чуть-чуть. Слайсы это сердце языка, это один из тех концептов, который делает Go крутым. Но все же, давайте все-таки представим, что как-то вовсе вдруг, в перерывах между разговорами о «концептах», мы захотим написать чуток реального кода. Вот то, что нам предлагает в данном случае язык Go:

// Давайте сделаем немного чисел...
numbers := []int{1, 2, 3, 4, 5}

log(numbers)         // 1. [1 2 3 4 5]
log(numbers[2:])     // 2. [3 4 5]
log(numbers[1:3])    // 3. [2 3]

// Интересный факт: отрицательные индексы не работают!
//
// numbers[:-1] из Python не прокатит. Взамен нам предлагают
// делать что-то вроде этой циркопляски с длиной контейнера:
//
log(numbers[:len(numbers)-1])    // 4. [1 2 3 4]

// “Потрясная” читабельность, Мистер Пайк! Хорош!
//
// А теперь давайте допишем шестерку:
//
numbers = append(numbers, 6)

log(numbers) // 5. [1 2 3 4 5 6]

// Самое время удалить из слайса тройку:
//
numbers = append(numbers[:2], numbers[3:]...)

log(numbers)    // 6. [1 2 4 5 6]

// Хочется вставить какое-то число? Ничего страшного,
// в Go есть общепринятая best practice!
//
// Мне особенно нравится это шапито с троеточиями ...
//
numbers = append(numbers[:2], append([]int{3}, numbers[2:]...)...)

log(numbers)    // 7. [1 2 3 4 5 6]

// Чтобы скопировать слайс, ты должен будешь написать это:
//
copiedNumbers := make([]int, len(numbers))
copy(copiedNumbers, numbers)

log(copiedNumbers)    // 8. [1 2 3 4 5 6]

// И это еще не все.

Хотите верьте, хотите — нет, но именно так гоферы каждый день трансформируют слайсы. А так как у нас нет никаких этих ваших дженериков, то написать красивую функцию insert(), которая будет прятать весь этот ужас просто не получится. Я залил этот рабочий пример нa playground, так что не стоит мне верить: можешь проверить своими руками.

Причина №2. Нулевые интерфейсы не всегда нулевые :)

Они говорят нам, что «ошибки в Go это больше, чем просто строки» и что мы не должны обращаться с ними, как со строками. Например, spf13 из Docker сказал об этом во время превосходного выступления на тему «7 common mistakes in Go and when to avoid them».
Они также говорят, что нам стоит всегда возвращать ошибки через интерфейс error (однородность, читабельность, и так далее). Это в точности то, что я делаю в сниппите кода ниже. Ты наверняка будешь удивлен, но эта программа действительно поздоровается с Мистером Пайком, но все ли так, как должно быть?

package main

import "fmt"

type MagicError struct{}

func (MagicError) Error() string {
	return "[Magic]"
}

func Generate() *MagicError {
	return nil
}

func Test() error {
	return Generate()
}

func main() {
	if Test() != nil {
		fmt.Println("Hello, Mr. Pike!")
	}
}

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

Причина №3. Забавное сокрытие переменных

В случае если вы не в курсе, что это такое, то давайте я все-таки процитирую Википедию: “сокрытие переменных происходит тогда, когда переменная определенная в определенном контексте (условный блок, метод, внутренний класс) имеет такое же имя, как и переменная во внешнем контексте”. Звучит толково, вроде как довольно известная практика, большинство языков программирования поддерживают сокрытие и вс окей. Интересно, но Go не исключение, но тут дела состоят несколько иначе: у нас есть оператор :=, который добавляет веселья. Вот как работает сокрытие переменных в Go:

package main

import "fmt"

func Secret() (int, error) {
	return 42, nil
}

func main() {
	number := 0

	fmt.Println("before", number) // 0

	{
		// meet the shadowing
		number, err := Secret()
		if err != nil {
			panic(err)
		}

		fmt.Println("inside", number) // 42
	}

	fmt.Println("after", number) // 0
}

Да, я в курсе, что оператор := создает новую переменную и задает ей значение по типу справа и то, что в соотв. со спецификацией языка это абсолютно корректное поведение. Но понимаете ли, стоит убрать внутренний контекст (фигурные скобки) и код заработает ровно так, как мы ожидаем («after 42»). Интересно девки пляшут, не так ли? В другом случае вам придется играться с замечательными сокрытием переменных.
Следует отметить, что это все не просто забавный пример, который я придумал за обедом, это реальная ловушка, в которую люди время от времени попадают далеко не по своей вине. На этой неделе я рефакторил немного кода на Go и встретил эту проблему дважды. Компиляторам все равно, линтерам все равно, всем все равно, но код работает неправильно.

Причина №4. Ты не можешь передать []struct как []interface

Интерфейсы классные, Pike&Co. продолжают говорить, что они это то, чем является Go: интерфейсы решают проблему дженериков, мы используем интерфейсы для тестов, через них в Go построен полиморфизм. Говорю вам, я полюбил интерфейсы прямо во время прочтения «Effective Go» и продолжаю их любить. Тем не менее, помимо проблемы «этот нулевой интерфейс не совсем нулевой», о которой я говорил чуть ранее, существует другая неприятная проблема, которая сконяет меня к мысли, что в Go нет полноценной поддержки интерфейсов. Простыми словами, ты просто не можешь передать слайс структур (которые удовлетворяют определенный интерфейс) в функцию, которая принимает слайс этого интерфейса:

package main

import (
	"fmt"
	"strconv"
)

type FancyInt int

func (x FancyInt) String() string {
	return strconv.Itoa(int(x))
}

type FancyRune rune

func (x FancyRune) String() string {
	return string(x)
}

// Удовлетворяет любой структуре с методом String().
type Stringy interface {
	String() string
}

// Строка, состоящая из строковых представлений всех элементов.
func Join(items []Stringy) (joined string) {
	for _, item := range items {
		joined += item.String()
	}

	return
}

func main() {
	numbers := []FancyInt{1, 2, 3, 4, 5}
	runes := []FancyRune{'a', 'b', 'c'}

	// Такое не прокатит!
	//
	// fmt.Println(Join(numbers))
	// fmt.Println(Join(runes))
	//
	// prog.go:40: cannot use numbers (type []FancyInt) as type []Stringy in argument to Join
	// prog.go:41: cannot use runes (type []FancyRune) as type []Stringy in argument to Join
	//
	// Взамен они предлагают нам заниматься вот такой циркопляской:
	//

	properNumbers := make([]Stringy, len(numbers))
	for i, number := range numbers {
		properNumbers[i] = number
	}

	properRunes := make([]Stringy, len(runes))
	for i, r := range runes {
		properRunes[i] = r
	}

	fmt.Println(Join(properNumbers))
	fmt.Println(Join(properRunes))
}

Совершенно неудивительно, что это известная проблема, которая даже проблемой-то не считается. Это просто очередная забавная штука в Go, окей? Я очень рекоммендую прочесть соотв статью в Wiki по теме, станет ясно, почему трюк с передаванием []struct как []interface не прокатит. С другой стороны, подумайте об этом! Мы можем это делать, тут нет никакой магии, это просто проблема компилятора. Посмотрите, прямо здесь, чуть выше в коде я это сделал вручную. Почему компилятор Go не может заниматься этим шапито вместо меня? Да-да, явное лучше чем неявное, но все же?

Я просто не могу терпеть то, как люди смотрят на дерьмо вроде этого, которого в языке просто полно и продолжают говорить «ну да, это норм». Это не норм. Это то, что делает Go ужасным языком программирования.

Причина №5. Неочевидные циклы «по значению»

UPD: Это несколько надуманная проблема, если смотреть со стороны языка. А если не смотреть, то все равно, т.к. мы же говорим о косяках непосредственно языка, а не документации и экосистемы.

Это вообще первая проблема, с которой я когда-либо столкнулся в Go. Окей, у нас тут есть «for-range» цикл, который позволяет ходить по слайсам и слушать каналы. Его используют повсюду и это окей. Вот вам еще одна незначительная, но не очень приятная проблема, с которой сталкивается большинство неопытных неофитов: range-цикл поддерживает итерирование только «по значению», он просто копирует значения и ты ничего не можешь с этим поделать — это тебе не foreach из С++.

package main

import "fmt"

func main() {
	numbers := []int{0, 1, 2, 3, 4}

	for _, number := range numbers {
		number++
	}

	fmt.Println(numbers) // [0 1 2 3 4]

	for i, _ := range numbers {
		numbers[i]++
	}

	fmt.Println(numbers) // [1 2 3 4 5]
}

Прошу отметить, я не ругаюсь на то, что в Go нет range-циклов «по ссылке», я ругаюсь на то, что эти range-циклы довольно неочевидны. Глагол “range” будто говорит “пройтись по элементам”, но не очень говорит “пройтись по копиям элементов”. Давайте посмотрим на секцию For из “Effective Go”, в ней ни слова про то, что range-циклы копируют значения слайса, там просто об этом не написано. Я согласен, что это очень незначительная проблема, к тому же я в свое время с ней очень быстро совладал (в течении минут), но неопытный гофер может потратить некоторое время на отладку кучка кода, не понимая, почему значения в массиве не меняются. Нужно было бы хоть немного, но обрисовать это дело в “Effective Go”.

Причина №6. Сомнительная строгость компилятора

Как я уже мог говорить ранее, Go считается чистым, простым и читабельным языком программирования со строгим компилятором. Например, у тебя не выйдет скомпилировать программу с unused import. Почему? Просто потому что Мистер Пайк считает, что так должно быть. Можешь верить, а можешь и нет, но unused import это далеко не конец Света и люди могут его пережить, особенно во время активной отладки. Я полностью согласен, что это не очень правильно и компилятор просто должно выводить какое-то предупреждение, но ради всего святого, какой смысл прекращать компиляцию из-за такой мелочи? Неиспользованный импорт, серьезно?

К Go1.5 нам подогнали классное изменение языка: наконец-то можно указывать элементы словаря без явного указания типа хранимого значения. Мальчикам из Go team понадобилось чуть более пяти лет, чтобы сообразить, что явное указание типа может быть чуток избыточным.

Другая замечательная штука, от которой я прусь в Go — это коммы. Видете ли, в Go у нас есть так называемые многострочные блоки объявления (import / var / const):
import (
    "fmt"
    "math"
    "github.com/some_guy/fancy"
)
const (
    One int = iota
    Two
    Three
)
var (
    VarName int = 35
)

Окей, но как только дело доходит до «читабельности», Роб Пайк решает, что надо ВНЕЗАПНО добавить запятые. В какой-то момент после добавления запятых, он решает, что стоит оставлять запятую на последней строчке списка. Таким образом, вместо того, чтобы писать так:
numbers := []Object{
    Object{"bla bla", 42}
    Object("hahauha", 69}
}

Мы вынуждены писать так:
numbers := []Object{
    Object{"bla bla", 42},
    Object("hahauha", 69},
}

Я до сих пор задаюсь вопросом, почему мы не можем просто обойтись без запятых на блоках import / var / const и уж никак не можем на списках или словарях. В любом случае, Роб Пайк точно шарит лучше меня, так что все окей. Да здравствует Читабельность!

Причина №7. Кодогенерация в Go это просто костыль

Во-первых, я не имею ничего против кодогенерацию. Для бедного языка, вроде Go, это, возможно, единственный способ избежать копипасты для обобщенных штук. В любом случае, go:generate — тулза для кодогенерации в Go, которая используется гоферами по всему миру, просто отстой. Ну, чтобы уж быть до конца честным, тулза сама по себе ок, мне нравится. Проблема не в тулзе, а в самом подходе. Смотри, для того, чтобы сгенерить какой-то код, тебе нужно воспользоваться специальным магическим комментарием. Да, магическая последовательность байтов где-то в комментариях твоего кода может генерить код! Поглядите только, какое зрелищное шапито мы наблюдаем ниже:

func Blabla() {
    // code...
}

//go:generate toolname -params -blabla

// code...

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

Эпилог


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

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

Пссс, парень, не хочешь форкнуть Go?
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 283
  • –79
    Это самый нелепый список придуманных проблем, которые я видел. Возмущаться тем, что тебе неудобны запятые в литералах и называть отсутствие встроенного дорогого копирования слайсов разных типов — плохим дизайном, попуская Пайка — это новый уровень Хабра?

    Илья, я знаю, что ты ещё учишься в школе, но зачем так позориться? Даже зная твою любовь к троллингу, это как-то слишком топорно.
    • +41
      numbers = append(numbers[:2], append([]int{3}, numbers[2:]...)...)
      

      И это необходимый минимум, чтобы вставить элемент в массив. Ты все еще считаешь, что Go совершенен?
      • –2
        А кто сказал, что вставка элемента в середину непрерывного куска памяти — тривиальная задача?
        • +47
          Вот только непонятно, почему она должна быть такой же сложной в коде.
          • –2
            Ну потому что это 3 операции на самом деле, а не одна:
            s = append(s, 0)
            copy(s[i+1:], s[i:])
            s[i] = x
            

            Но я согласен, можно было и сделать удобный метод вставки по позиции.
            • +17
              Это технически три операции, а семантически — одна. Что мешало сделать insert(numbers, i, v)?
              • –34
                Тем, что городить новый keyword ради спорной нужности операции — это именно то, от чего в Go старались уйти.
                • +25
                  К сожалению, вся остальная продемонстрированная работа со слайсами тоже не блещет.
                  • –40
                    Окей. Пишите на том, где вам блещет, к чему все эти сотни комментариев про «не блещет».
                    • +23
                      Ну так, иногда хочется и с окружающими поделиться своими впечатлениями. Можно подумать, вы из других побуждений статьи пишете.
                      • –29
                        Конечно из других. Я делюсь знаниями в статьях. А вы в комментариях хейтите то, чем не пользуетесь. Это разные вещи.
                  • +25
                    Почему бы не сделать это в виде библиотечных функций, как это везде повсюду? Как же знаменитый code-reuse? Из языка вырезали так много, что приходится обратно все туда руками заталкивать. И это будет не стандартный API, а велосипед, который все и каждый делают заново. Это — плохо продуманный язык. Кто-то слишком фанател идеей о «спорной нужности операции».
                    • –17
                      Приведите три реальных примера, когда Вам нужно засовывать элементы в произвольное место в массиве/слайсе. Реальные кейсы только, пожалуйста. У меня, к примеру, за последние 2 года таких кейсов не было.

                      Поскольку вы утверждаете, что это крайне нужная операция, и авторы Го просто недостаточно умны, в отличие от школьников, чтобы понять это, это не должно составить труда.
                      • +16
                        Добавление элемента в сортированный список.
                        • –19
                          Если нужен сортированный список — используйте linked list и сортируйте при вставке, слайсы тут причем?
                          Еще примеры? Реальные, из ежедневных проектов, которые вы пишете, желательно.
                          • +11
                            Часть алгоритмов сортировки. Те же деревья (не все они состоят из связанных списков), многие производят манипуляции с массивом в памяти, потому что всё равно всё упрётся в диск. Опять же, связанный список требует дополнительное место на хранение указателя на следующий объект. Массив быстрее связанного списка на ряде задач, например, взятие элемента i в случае массива — операция со сложностью 1, а в случае списка 1-N.
                            • –23
                              Хорошо, котируется — специфические структуры данных. Вопрос — как часто вы пишете свои структуры данных? Сколько раз в неделю? В месяц? В году?

                              Сколько процентов от вашего основного кода занимает разработка своих структур данных? Оригинальный вопрос в силе — приведите 3 реальных последних примера из своего опыта этой «высокой нужной операции». Вопрос без подвоха.
                              • +1
                                Я пишу на js, но вы правы, это действительно редкая операция, последний раз использовал вставку в середину массива примерно в апреле, когда реализовывал те самые b+tree для серверсайда.
                                • 0
                                  Много, я занимаюсь машинным обучением и написанием высокопроизводительных систем с этим делом. Месяц назад писал кусок, где нужно было именно это, например.
                                  • +1
                                    Но относительно циклов или любой другой операции — вставка в середину действительно редка.
                                    • +1
                                      Что такое «вставка в середину относительно циклов»?
                                  • +6
                                    > Вопрос — как часто вы пишете свои структуры данных?

                                    простите, а вы на работе чем занимаетесь? То есть я не понимаю как вообще писать программы больше 1000 строк, не создавая структур данных.
                                • +1
                                  старая изжеванная проблема — LinkedList vs ArrayList
                                  Далеко не всегда связные списки хороши, к тому же в памяти гадят, а с учетом, что язык с со сборкой мусора…
                                  • +4
                                    Забавно, что за более чем десять лет программирования мне ни разу не приходилось использовать linked list ни в геймдеве, ни в контестах, вообще нигде, кроме как в примерах первого учебника по ООП. Уж насколько медленная эта структура, учитывая структуру кэша памяти! Кстати, рекомендую к прочтению «What every programmer should know about memory»
                                    • +5
                                      Какая сложность нахождения элемента в сортированном linked list, меньшего или равного данному? Бинарным поиском-то уже не попрыгаешь.

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

                                      Извините за резкость, просто когда предлагают делать сортированные массивы на связных списках, это как-то перебор.
                                  • 0
                                    Разнообразные отсортированные очереди структур данных. Добавление должно происходить не в конец очереди, а в место, определенное каким-то полем структуры, например таймстампом.
                                    • 0
                                      Придумать кейс можно, безусловно. Тут у меня те же вопросы: почему именно слайс и как часто вы пишете свои очереди структур данных? Речь о компромиссах и приоритетах. И да, все еще хочу увидеть по три реальных примера этого «частого важного кейса» из реальной практики.
                                      • +4
                                        Я просто объединил свои частые кейсы. Реальные примеры, которыми я недавно занимался — очередь заявок на обработку оператором (заявки должны обрабатываться в сложном порядке, новая заявка далеко не всегда вставляется в конец очереди), очередь исполнения бизнес-транзакций по сущности (некоторые транзакции добавляются задним числом), очередь сообщений, пришедших на сокет, которые должны обрабатываться тоже не в порядке поступления. Трех достаточно?
                                        • +1
                                          Может пора задуматься над несколькими очередями с разными приоритетами вместо того, чтобы использовать очередь таким образом, что она перестают быть очередью?
                                          • 0
                                            Бывает логика приоритетов сложнее, чем в тривиальной priority queue. Взять, например, какой-нибудь deadline scheduling.
                                            • +1
                                              А почему сложная логика должна реализовываться посредством такого прямолинейного и простого инструмента как слайс, созданного для решения совершенно других кейсов? Для того, чтобы потом рассказывать какой это плохой инструмент?
                                            • 0
                                              Очереди с разными приоритетами был второй этап развития после обычной очереди. В перспективе — очереди, динамически изменяющиеся, а не с определением места при вставке.
                                              • 0
                                                Да все понятно уже. Ваша бизнес логика давно переросла функционал слайсов, но вместо того чтобы это осознать и использовать другую структуру данных (связный список тут явно напрашивается) вы продолжаете есть кактус и утверждать, что использование слайсов для этой задачи оправданно и типично.
                                                • +2
                                                  связный список тут явно напрашивается

                                                  Иногда можно уперется в память, т. к. в linked list хранятся указатели на предыдущий и следующий элементы.
                                                  И linked list не поддерживает случаный доступ ( привет, O(n) вместо O(1) )
                                                  Стоп, кажется, это уже было сказано здесь
                                                  Да все понятно уже

                                                  • 0
                                                    То есть возможность упереться в память при увеличении размера слайса вас не заботит (а это хоть и временное, но двукратное увеличение!) и вы не видите в этом проблемы, а вот пара лишних указателей (или даже один — списки бывают еще и односвязными, что в случае очереди кажется более оптимальным) — это большая проблема?
                                                    • +1
                                                      Да и тут речь даже не в том, какой способ лучше. Речь о том, что необходимость таких изощренных манипуляций по определению не может быть простой и не является типичным примером использования. В случае необходимости конечно придется повозиться, но это жизнь, так сказать. Но в 99% случаев при правильном подходе нет необходимости вставлять элементы в середину слайса и в этих 99% случаев слайсы в Пщ очень удобны и лаконичны.
                                                      • 0
                                                        То есть возможность упереться в память при увеличении размера слайса вас не заботит (а это хоть и временное, но двукратное увеличение!)

                                                        Не знал, что такая реализация ( это получается vector из C++ ). Тогда замечание про память невалидно.
                                                        • 0
                                                          Такая проблема ArrayList'а есть, она известна, но на практике, бездумное использование ArrayList явно говорит о неверном дизайне. С другой стороны, раз выделив шматок памяти под слайс(с запасом, конечно), мы спокойно вставляем элемент в нужное место — происходит сдвиг элементов, без выделения памяти. Но когда нужен быстрый произвольный доступ — это вариант. Если же у нас миллион элементов в связном списке, то наложится и работа сборщика мусора и отсутствие произвольного доступа и тп.
                                                          Где-то лучше связный список, где-то массив, эта тема уже 100500 раз поднималась, и все достоинства и недостатки подходов давно известны. Уже не раз сталкивался с тем, что стараются избавиться от указателей и делать всё на массивах, ибо на но больших объемах сборщик мусора просто не дает нормально работать, что в C#, что в Go. Но спор то не об этом.
                                                          • +1
                                                            У связных списков есть другое проблемное место (особенно в concurrent окружении), они сильна трешат prefetch кэша, что может приводить к потере 1-2 порядков. Исключение — это выделение блоками (или одним потоком), чтобы все аллоцированные элементы связного списка лежали последовательно и давали предсказуемый memory access pattern.
                                              • 0
                                                Хм, а почему для таких очередей не использовалась пирамида?
                                                • 0
                                                  Потому что профайлер показал, что так медленнее, или бегать по ней потом медленно.
                                          • +6
                                            позаимствованный Oberon-Way не всегда единственно правильный.
                                    • +5
                                      А кто сказал, что это сложная задача? Даже на С она решается тривиально и я даже осмелюсь сказать, что получится читабельнее этого месива из скобок и точек. Это не говоря о том, что в современных языках это обычно входит в стандартную библиотеку.
                                      • –1
                                        Я б написал так
                                        memmove(&a->array[position+1], &a->array[position], &a->array[a->used] - &a->array[position]);
                                        

                                        И да, я тоже считаю, что это читабельнее.
                                        • +5
                                          И чем оно читаемее, чем код ниже?

                                          a = append(a[:position], append([]T{x}, a[position:]...)...)
                                          
                                          • +9
                                            идеальный вариант ИМХО
                                            a.insert(position, value);
                                            • +17
                                              Подброшу-ка я дровишек: именно так и выглядит аналогичный код на Rust. :)
                                              • –1
                                                Это же vec, а не array
                                                • +10
                                                  А чем array, который вы имеете ввиду, отличается от Vec в Rust?
                                              • –1
                                                Написал на коленке вариант враппера для Go: play.golang.org/p/OgzWfYeB1V
                                                Здесь нет проверок границ и многого другого, но все красиво обернуть при желании тут тоже можно.
                                                Конечно другое дело, что все это ложится на плечи конечного программиста: либо писать реализацию самому, либо использовать готовые библиотеки.
                                                • +17
                                                  Библиотека для вставки элемента в середину массива – это прекрасно! Сразу вспомнилось это:

                                                  image
                                                  • +1
                                                    Ну а что делать?
                                                    Люди требуют, чтобы было
                                                    a.insert(position, value);
                                                    

                                                    А потом туда можно свистелок напихать еще разных.
                                                    В C++ std тоже включает много всего разного.
                                                  • +1
                                                    Угу…

                                                    func main() {
                                                    	d := []int{1,2,3,4}
                                                    	s := NewHandySlice(d)
                                                    	s.Insert(2, "abc")
                                                    	fmt.Println(s.Values()[1].(int))
                                                    	fmt.Println(s.Values()[2].(int))
                                                    }
                                                    


                                                    Компилируется (еще бы), но при запуске (очевидно), выдает:

                                                    2
                                                    panic: interface conversion: interface is string, not int
                                                    
                                                    • 0
                                                      Я же написал, что нету проверок никаких, это пример обертки.
                                                      Вы хотите production-ready код?

                                                      • +6
                                                        Я хочу, чтобы проверки были compile-time, а не run-time. Это возможно?
                                                        • 0
                                                          В данном варианте нет.
                                                          • +3
                                                            Вот это и проблема. А будет ошибка на Insert или на приведении — уже не так важно.
                                                            • +1
                                                              Согласен. В С++ это можно было бы решить шаблонами. А вот в С точно так же only runtime checking.
                                                              • +12
                                                                А в некоторых языках просто есть дженерики.
                                                                • 0
                                                                  Может и здесь скоро появится. «Некоторые» языки тоже не сразу их заимели.
                                                                  • +4
                                                                    То-то нам (почти) в каждом посте про Го дают ссылку на этот же документ как на объяснение, почему в Го дженериков не будет и это правильно.
                                                                    • +3
                                                                      Андрей Александреску обмолвился о generics в Go в одном из CppCast:
                                                                      «It became the n-word of Go. Like you can't say 'generics' with 'Go', because everybody is gonna be offended in Go communitiy»

                                                                      https://www.reddit.com/r/programming/comments/3qs888/cppcast_d_with_andrei_alexandrescu
                                                                      Для тех, кто не знает, «n-word» — это эвфемизм слова «nigger» в США.
                                                                      • +1
                                                                        <занудство>«n-word» — это не эвфемизм слова «nigger», а название слова «nigger». Эвфемизмы — например, «dark-skinned» — используются для обозначения негров, а не слов.</занудство>
                                                                        • 0
                                                                          Точно, спасибо за исправление.
                                                • –2
                                                  mirrr
                                                  Поправьте меня если я не прав, но в вашем примере создаётся новый массив.
                                                  Это значит, что будет новое выделение памяти и будет дополнительная нагрузка на GC.
                                                  В некоторых приложениях это не приемлемо

                                                  defuz
                                                  Отличия в том, что это структура данных построенная на array, которая удобнее, но все же в памяти она занимает больше. В общем все тож самое что и в плюсовом std.
                                                  • 0
                                                    Тот факт, что мы активно обсуждаем операцию insert наталкивает меня на мысль о том, что видимо эти самые array в go являются growable (динамические). Если это так, то это мало чем отличается от векторов в C++ и Rust.

                                                    В каком языке векторы построены поверх array?
                                                    • 0
                                                      Скорее всего вы права, можно сравнить go array с rust vect.

                                                      В Java Vector реализован на Array, в С++ насколько я помню тоже.
                                                      • +2
                                                        В java Vector — давно устаревшее говно мамонта. А актуальный ArrayList работает поверх массивов, с динамическим адаптивным расширением в зависимости от размера данных. В openjdk8 этот массив может быть либо пустым (константой), либо размером не менее 10 элементов. При небольших размерах он увеличивается с шагом в 50% от текущего размера.
                                          • –33
                                            Я не мыслю категориями «совершенен»/«не совершенен», это оставим школьникам. Вставка в произвольное место — это by design дорогая операция, вынуждающая двигать память, но 99% кейсов использования слайсов — это append. Это называется «компромисс», и все что касается дизайна языка — это всегда о компромиссах. То, что в слайсах в Go так неудобно делать произвольную вставку — создает стимул думать головой о том, какая структура данных больше подойдет, если нужно делать много быстрых произвольных вставок, а не вслепую ворочать память.

                                            В любом случае, школьник тыкающий в Пайка и Томпсона, «объясняющий» им своим невежством, какой плохой они придумали дизайн — это мне непонятно.
                                            • +31
                                              школьник тыкающий в Пайка и Томпсона, «объясняющий» им своим невежством, какой плохой они придумали дизайн — это мне непонятно

                                              Ваня, ты наверное про нигилизм не слышал. Я не спорю, что Пайк и Томпсон крутые чуваки, но давай они хоть как-то будут объяснять свои решения. Удел разумного человека не идти на поводу у авторитетов, а делать соотв умозаключения, которые приведут его к истине.
                                              • +16
                                                Что вы заладили — «школьник», «школьник»… у вас школьник пирожок отобрал, что ли?
                                                • +4
                                                  Поддерживаю. Если автор статьи и учится в школе, то это совершенно неважно — он в свои годы уже намного умнее и рассудительнее чем половина моих бывших одногруппников.
                                                • +1
                                                  У вас судя по всему какой-то комплекс, на тему школьников, лол ;)
                                                  • –2
                                                    Как то возник вопрос «а как искать элемент в срезе?», возник ответ «перебором».
                                                    И вот что из этого вышло:
                                                    Пример поиска в срезе
                                                    package main
                                                    
                                                    import "fmt"
                                                    
                                                    type IndexTeller interface {
                                                    	Index (value interface{}) (pos int)
                                                    }
                                                    
                                                    type MySlice []interface{}
                                                    
                                                    func (s MySlice) Index(value interface{}) (pos int) {
                                                    	for pos, e := range s {
                                                    		if e == value {
                                                    			return pos
                                                    		}
                                                    	}
                                                    	return -1
                                                    }
                                                    
                                                    func main() {
                                                    	arr_i := MySlice{1, 2, 3, 4, 5}
                                                    	arr_s := MySlice{"a", "b", "c", "d", "e"}
                                                    	arr_m := MySlice{MySlice{1, 2, 3}, MySlice{"a", "b", "c"}}
                                                    
                                                    	var i int
                                                    
                                                    	i = arr_i.Index(2)
                                                    	fmt.Println(i)
                                                    
                                                    	i = arr_s.Index("c")
                                                    	fmt.Println(i)
                                                    
                                                    	i = arr_m.Index(MySlice{1, 2, 3})
                                                    	fmt.Println(i)
                                                    }
                                                    


                                                    Вывод
                                                    [guest@localhost go]$ go run t.go 
                                                    1
                                                    2
                                                    panic: runtime error: comparing uncomparable type main.MySlice
                                                    
                                                    goroutine 1 [running]:
                                                    main.MySlice.Index(0x18335f50, 0x2, 0x2, 0x80e5400, 0x183380a0, 0x0)
                                                            /home/guest/tmp/tests/misc/go/t.go:13 +0x90
                                                    main.main()
                                                            /home/guest/tmp/tests/misc/go/t.go:33 +0x89e
                                                    
                                                    goroutine 2 [runnable]:
                                                    runtime.forcegchelper()
                                                            /usr/lib/golang/src/runtime/proc.go:90
                                                    runtime.goexit()
                                                            /usr/lib/golang/src/runtime/asm_386.s:2287 +0x1
                                                    
                                                    goroutine 3 [runnable]:
                                                    runtime.bgsweep()
                                                            /usr/lib/golang/src/runtime/mgc0.go:82
                                                    runtime.goexit()
                                                            /usr/lib/golang/src/runtime/asm_386.s:2287 +0x1
                                                    
                                                    goroutine 4 [runnable]:
                                                    runtime.runfinq()
                                                            /usr/lib/golang/src/runtime/malloc.go:712
                                                    runtime.goexit()
                                                            /usr/lib/golang/src/runtime/asm_386.s:2287 +0x1
                                                    exit status 2
                                                    [guest@localhost go]$
                                                    


                                                    Ну, ты понял, да, когда надо будет писать программу, ты будешь не программой заниматься, а размышлять, а как же её написать в Go.
                                                  • +3
                                                    Меня больше всего поразило, что нельзя определить метод для этого. А еще кто-то недавно гнал на Ruby с его monkey-patching…
                                                    • 0
                                                      Я не совсем в теме, почему нельзя?
                                                      • +4
                                                        Дженериков нет. Этот метод придётся копипастить для каждого типа, используемого в массивах.
                                                        • 0
                                                          А что мешает использовать интерфейсы?
                                                          • +3
                                                            Необходимо определить операцию не над определённым типом, а над массивом таких типов, интерфейсы здесь причём?
                                                            • +1
                                                              есть способы с использованием стандартного пакета «reflect», но это конечно не очень круто, дженериков не хватает реально
                                                              • +4
                                                                Рефлексия — это не «не очень круто», это «очень не быстро».
                                                              • 0
                                                                Я об []interface.

                                                                Если существует такая частая необходимость вставки в вашей структуре данных, да еще со множеством различных типов, то почему не сделать тип-обертку над []interface{}

                                                                Как-то так
                                                                package main
                                                                
                                                                import (
                                                                	"fmt"
                                                                )
                                                                
                                                                type arr []interface{}
                                                                
                                                                func (a *arr) insert(pos int, x interface{}) {
                                                                	*a = append((*a)[:pos], append([]interface{}{x}, (*a)[pos:]...)...)
                                                                }
                                                                
                                                                func main() {
                                                                	a := arr{1, 2, 3, 4, 5}
                                                                	a.insert(2, 77)
                                                                
                                                                	fmt.Println(a) 
                                                                 	// out: [1 2 77 3 4 5]
                                                                }
                                                                



                                                                Если же планируется применять в паре типов, то сойдет и так:
                                                                func insert(a *[]int, pos int, x int) {
                                                                	*a = append((*a)[:pos], append([]int{x}, (*a)[pos:]...)...)
                                                                }
                                                                
                                                                • +6
                                                                  Если существует такая частая необходимость вставки в вашей структуре данных, да еще со множеством различных типов, то почему не сделать тип-обертку над []interface{}

                                                                  Потому что преимущества строгой типизации теряются.
                                                        • +3
                                                          Э… Всмысле нельзя? Т.е. вообще нельзя? А как жить, если надо? Можете для человека, который вообще не знает Go, коротко объяснить что именно нельзя и почему?
                                                          • +3
                                                            Нельзя определить метод insert, который будет работать с любым массивом. Потому что массив целых чисел и массив строк — это разные типы данных, один метод не может работать с ними обоими.
                                                            • 0
                                                              Потому что массив целых чисел и массив строк — это разные типы данных, один метод не может работать с ними обоими.

                                                              Может. Но есть обратная сторона медали, как описали ниже. Невозможно проверить тип во время компиляции, например.
                                                        • –2
                                                          Ты ведь в курсе что можно оборачивать код который тебе не нравится в функции с более красивым дизайном? Го упрощен до невозможности, автор не ставил целью чтоб ты кончал когда пишешь на го, он упростил все действия, чтоб увеличить простоту и скорость, добавляя безопасности. Ты пишешь на голом языке, но избавлен от слежки за кодом постоянным. Со временем появятся и конструкции более менее универсальные, но сейчас цель у авторов отнюдь не идеальная красота кода, и они прямо об этом пишут.
                                                          • +5
                                                            Ты ведь в курсе что можно оборачивать код который тебе не нравится в функции с более красивым дизайном?

                                                            О, а как в Go обернуть в функцию код для вставки элемента в середину слайса произвольного типа (с сохранением типобезопасности, конечно)?
                                                            • –1
                                                              Функция не идеальна, но я без малого три дня как начал вникать в go, может более опытные могут сделать лучше? То же можно сделать и в обертке, которую я приводил выше, там был ваш вопрос о типизации.

                                                              func insert(s interface{}, pos int, x interface{}) {
                                                              	switch a := s.(type) {
                                                              	case *[]int:
                                                              		*a = append((*a)[:pos], append([]int{x.(int)}, (*a)[pos:]...)...)
                                                              	case *[]byte:
                                                              		*a = append((*a)[:pos], append([]byte{x.(byte)}, (*a)[pos:]...)...)
                                                              	case *[]string:
                                                              		*a = append((*a)[:pos], append([]string{x.(string)}, (*a)[pos:]...)...)
                                                              	// case ...
                                                              	}
                                                              	return
                                                              }
                                                              
                                                              • +6
                                                                Во-первых, типобезопасность не сохранена — если я передам s одного типа, а x — другого, будет ошибка приведения.
                                                                Во-вторых, не будет работать для произвольного (неизвестного на момент написания функции) типа.
                                                                • –10
                                                                  В Go УЖЕ есть универсальная встроенная функция манипулирования слайсами, которая работает с любыми типами и с максимальной скоростью — append. И не нужно ничего изобретать, все уже изобретено и реализовано. Если вам не нравится синтаксис — это ни как не проблемы языка. Более того, так и задумано. Неужели вы считаете, что авторы оказались не способны привнести в синтаксис сахарку, если бы посчитали это необходимым? Я вас уверяю, смогли бы. Тот факт, что эта функция реализована именно таким образом говорит о том, что авторы языка считают именно такой синтаксис наиболее подходящим и я лично с ними в этом солидарен. Этот синтаксис показывает программисту, что операция которую он совершает, не простое присваивание, а ресурсоемкая задача. Нравится вам это или нет, но этот язык именно такой. Если он вам не подходит — не пишите на нем. Но не надо говорить, что язык плох из-за того, что он для вас недостаточно подслащен. Расслабьтесь и наслаждайтесь жизнью в сторонке от Go.
                                                                  • +4
                                                                    Спасибо за просвещение.

                                                                    Теперь поднимитесь на пару уровней выше по ветке и посмотрите, на какой именно комментарий я отвечал.
                                                                    • +10
                                                                      Этот синтаксис показывает программисту, что операция которую он совершает, не простое присваивание, а ресурсоемкая задача.

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

                                                                      Расслабьтесь и наслаждайтесь жизнью в сторонке от Go.

                                                                      А давайте вы лучше будете жить спокойнее в сторонке от тем, который так вас подрывают. Нравится — ок. Нам не нравится — извольте нам дать свободу для обсуждения. На любой язык ругаются и это позволяет сделать их лучше. На Страуструпа с плюсами жалуются — получаем новые стандарты. На команду C# жалуемся — получаем новые версии языка. Авось и до команды Go дойдет, что дизайн языка таки не блещет. В нем много хорошего, но достаточно проблемных мест, которые никак не подорвут его философию.
                                                                    • –2
                                                                      Точно, не заметил.

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

                                                                      Думаю остановлюсь на варианте со строгой типизацией и возможностью реализации для каждого типа, для которого понадобится использование функции в приложении.
                                                                  • 0
                                                                    Отвечу Вашими же словами :)
                                                                    Спасибо за просвещение.

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

                                                                    У человека выше подгорело из-за синтаксиса
                                                                    И это необходимый минимум, чтобы вставить элемент в массив. Ты все еще считаешь, что Go совершенен?

                                                                    А не из-за того что в go нет шаблонов. Как бы тоже следите за цепью диалога, а потом свои несчастные 5 коп вставляйте.
                                                                    • +3
                                                                      Вот прямо в исходном посте все про это написано: «А так как у нас нет никаких этих ваших дженериков, то написать красивую функцию insert(), которая будет прятать весь этот ужас просто не получится.»

                                                                      Для меня это очень логичный ход мысли: уродливый синтаксис — написать что-нибудь, что его прячет — что, мне это писать для каждого типа?! Причем это не выдуманное, я по этой тропинке прыгал во времена .net 1, где напишешь какую-нибудь удобную мулечку, а потом ррраз — и вынужден ее повторять еще для пары разных типов.

                                                                      (причем даже когда дженерики случились, тоже было больно, потому что операции сложения они, скажем, не покрывали, и приходилось отдельно прыгать)
                                                                      • +2
                                                                        Да, придется повторять, с этим то никто и не спорит, но я не пойму, почему не сказать прямо (я напомню я отвечал не напрямую посту, а комментарию) «меня бесит копипаст, не хочу повторять код». Вместо этого приводят просто длинный и уродливый кусок кода со словами «смотри какой уродливый код мне приходится писать?». Об этом речь а не о типе и невозможности написать универсальный для типа код.
                                                                        • 0
                                                                          Это самоподдерживающаяся аргументация. Копипасты приходится писать, потому что язык не позволяет сделать универсальное решение; универсальное решение нужно потому, что кусок кода, решающий задачу, уродлив. Выдерните любой пункт — остальная боль тоже пропадет, потому что можно будет одно за другим спрятать.
                                                                          • –1
                                                                            Нет, это называется вброс, или «непрямой аргумент». Когда автор специально искажает данные для пущего эффекта. Ибо прямые указания будут иметь меньший эффект. Если я скажу «не люблю копипастить, были бы дженерики тут», это все поддержат, ибо никто этого не любит, это очевидная проблема, и никто даже обсуждать это не будет, про нее все знают, но если я скажу «язык уродлив и не позволяет мне делать мои элегантные конструкции, которые давно уже есть в других языках». Вроде смысл тот же, но бомбить начинает у людей уже на много сильней. Я не приветствую такие провокационные фразы от других.
                                                                            • +2
                                                                              Если я скажу «не люблю копипастить, были бы дженерики тут», это все поддержат, ибо никто этого не любит, это очевидная проблема, и никто даже обсуждать это не будет, про нее все знают

                                                                              Ох, я был бы рад, если бы это было так.
                                                                              • +1
                                                                                В том то и дело, в среде любителей го этот вопрос обсуждается больше всего, это очень наболевшая тема и все были бы счастливы если бы эта фича наконец-то появилась. Я даже не знаю кто может отрицать что в го этого не хватает. Как и адекватной модульной системы. Я очень сомневаюсь что адекватный человек, знающий GO будет спорить про эти две вещи.
                                                                                • 0
                                                                                  А потом придет divan0 и скажет, что создатели языка все уже придумали и дженерики не нужны, а этой задачи вообще не существует.
                                                              • +9
                                                                Только вот я это читаю и просто диву даюсь — да это ж пересказ всего того мата, который был у меня, когда я начинал писать на Go. Простая примитивная программа из-за тех же слайсов и чехорды между [n]int и []int превращается в такое убожество, что страшно. Язык простой, да, но код на нем получается совсем не простой.
                                                                • +23
                                                                  Во-первых, зачем же ограничиваться обсуждениями школьник/не школьник, давайте обсудим национальность и сексуальные предпочтения автора поста.
                                                                  Во-вторых, это перевод.
                                                                  В-третьих, если у языка с точки зрения программиста есть недостатки, почему бы ими не поделиться? Если это будет субъективная неприязнь данного программиста, статья сгинет в Лету, если многих это беспокоит, почему не обсудить? Спокойно и конструктивно. После ваших комментов возникат одна мысль — «Бомбануло».
                                                                  • +7
                                                                    Во-вторых, это перевод.

                                                                    Это авторский перевод, кстати.
                                                                  • +5
                                                                    Судя по традиционному срачу в коментах, Go это новый JS
                                                                  • –43
                                                                    Ну окей, человеку не нравятся эти моменты языка, он это рассказал в своём блоге. Но зачем мне-то знать, что ему не нравится?
                                                                    • +16
                                                                      Прошу заметить как автор элегантно в первом предложении снял с себя ответственность под видом перевода. А перевод-то своего собственного поста :)
                                                                      • +4
                                                                        Решил покопаться ещё. Автор, опять же, сам лично разместил ссылку на реддите на свой же пост: www.reddit.com/r/programming/comments/3qjo3y/why_go_is_a_poorly_designed_language_from_a. И «обширность» обсуждения на реддите спровоцирована не статьёй, а ложными фактами, отсебятиной и враньём в цитатах Роба Пайка у автора (люди потрудились, поискали и выяснили, что он такого не говорил).
                                                                        В общем, не стоит попадаться на грязные уловки автора и мнимый авторитет самого себя :)
                                                                        • –1
                                                                          У меня в статье нет ни одной цитаты Пайка, о чем ты?
                                                                          • 0
                                                                            Первое предложение третьего абзаца в оригинальной английской статье. Пайк такого не говорил и даже не подразумевал.
                                                                            • 0
                                                                              Он говорил как-то так:

                                                                              ...And yet, with that long list of simplifications and missing pieces, Go is, I believe, more expressive than C or C++. Less can be more.

                                                                              But you can't take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.

                                                                              We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally.
                                                                              • 0
                                                                                Ну смотри, там я действительно облажался — я не хотел цитаты, я хотел его несколько перефразировать. Там была ссылка на его статью, где он после списка вещей (огромного списка), которые они из Go убрали, говорит: «But you can't take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.»

                                                                                Это кагбэ намекаэт, что он пытался убрать все по-максимуму, так что я не очень-то и соврал. С другой стороны — да, нехорошо получилось. Но ничего, я кавычки из оригинала убрал и заменил said на according to, спасибо за фидбэк.
                                                                      • –46
                                                                        Ооо, началось!

                                                                      • +1
                                                                        Стиль неуместный.
                                                                        • –10
                                                                          Простите, мистер зануда, я больше так не буду.
                                                                          • +4
                                                                            Зря вы считаете, что ваши фамильярные выпады на Пайка и закос под нейтив инглиш спикера в оригинале — это сколь нибудь оригинально или остроумно.
                                                                            • –25
                                                                              Я тебе даже больше скажу: мало того, что я так считаю, я глубоко убежден, что мои выпады на Пайка и украшения в английским — это очень оригинально и обосрацца как остроумно! Кстати, зашел к тебе в профиль минус поставить, а там уже… И заметки нет — не знаю, за что. Не напомнишь?
                                                                              • +9
                                                                                Ну так-то зачем?.. Всё впечатление об адекватности теряется сразу.
                                                                          • +1
                                                                            Нормальный стиль. Главное ведь не стиль, а чтобы информация усваивалась мозгом максимально быстро. Можно писать скучную документацию в официальном стиле, а можно вот так. Какой вариант усвоится лучше?
                                                                            • +2
                                                                              Быстрее усвоится то, что не отвлекает на форму, если уж на то пошло.
                                                                          • –17
                                                                            Для меня лично некоторым гарантом юзабельности Go является его использование в том же ВК (статьи были на Хабре). Но чему-то слишком разрекламированному у некоторых людей часто возникает отторжение. Да простит меня хабр, но именно так произошло, например, с фильмом «50 оттенков серого» — чрезмерная реклама и разочарование от обыденности. Статья — наглядный пример такой реакции.
                                                                            • +1
                                                                              Это может ничего не значить. Допустим, у меня есть нагруженный проект. И нужно поправить некий батлнек производительности, максимально оптимизировать. И предположим, что в силу сложившихся обстоятельств наилучшую производительнсть мне дает Brainfuck (обеспечивая необходимую стабильность и не порождая других проблем). Конечно же, я напишу это на нем. Но это не говорит ничего о юзабельности языка. Лишь о том, что у него есть свои сильные стороны и что ситуативно имеет смысл его использовать.

                                                                              И это лишь один из вариантов, почему язык %language% используется в %project%.
                                                                              • +1
                                                                                Во да, мы Go выбрали именно для тех задач, где он проявляет свои сильные стороны.
                                                                                • 0
                                                                                  Лишь о том, что у него есть свои сильные стороны и что ситуативно имеет смысл его использовать

                                                                                  Ну да, значит язык применим на практике (пусть даже в определенном круге задач), то бишь юзабелен. Я особо ничего больше и не имел ввиду. Если не ошибаюсь, то как раз AterCattus рассказывал в презентации про то, как хорошо Go был применим для задачи, подчеркивая отсутствие желания переписывать всю логику сайта на этом языке. Полностью согласен.
                                                                                  • 0
                                                                                    Конечно бесмысленно пытаться переписывать то, что и так хорошо работает. Go для тех задач, где его особенности проявляют себя как сильные стороны по сравнению с тем же C/C++.
                                                                                • –8
                                                                                  Вообще считать ВК авторитетным глупо. Они не использовали ООП при написание такого большого, сложного по структуре и высоконагруженого проекта.
                                                                                  • +15
                                                                                    Если команда ВК смогла написать проект такого уровня без использования ООП, то я скорее задумаюсь о необходимости ООП, чем о их криворукости. Я никогда не работал с ВК кроме как пользователь, но мне он не показался бажным или неповоротливым.
                                                                                    • 0
                                                                                      мне он не показался бажным или неповоротливым.

                                                                                      <offtop>
                                                                                      Давненько вы им не пользовались :)
                                                                                      </offtop>
                                                                                      • –2
                                                                                        Человек, поставивший минус, видимо, никогда не пытался смотреть видео или гифки вконтакте.
                                                                                        • +1
                                                                                          никогда не пытался смотреть видео или гифки вконтакте

                                                                                          Не могли бы развить свою мысль, а то не совсем понятно что имели ввиду.
                                                                                          • –2
                                                                                            Ну, гифки грузятся медленнее, чем видео на ютубе, а видео вконтакте вообще в половине случаев не проигрывается. И проблеме этой не один год, насколько я помню.
                                                                                • +6
                                                                                  Пятая проблема надумана — я не слышал ни про один язык программирования, где были бы циклы foreach «по ссылке» (кроме, наверное, C++ — там я немного отстал от мейнстрима).
                                                                                  • +3
                                                                                    Аналогичный код на Swift не компилируется: «cannot pass immutable value to mutating operator: 'number' is a 'let' constant». Кажется, автор этого просит, а не циклов по ссылке.
                                                                                    • +2
                                                                                      Ха. Просто так не компилируется? Да кого это останавливало!
                                                                                      var numbers = [0, 1, 2, 3, 4]
                                                                                      
                                                                                      for var number in numbers {
                                                                                          number++
                                                                                      }
                                                                                      
                                                                                      print(numbers) // [0, 1, 2, 3, 4]
                                                                                      


                                                                                      Никогда ещё каменты в интернете не отзывались мне так быстро: оказывается, умудрился неделю назад посадить баг именно на Swift и именно этим способом.
                                                                                    • –1
                                                                                      Хотел про это же написать, но автор (частично) прав, например если перебирать массив объектов (в питоне), то эти объекты можно будет изменять, в го они копируются, пример.
                                                                                      • +1
                                                                                        В питоне объекты всегда передаются по ссылке на них, в го — всегда копируются. Циклы тут ни при чем: play.golang.org/p/B5Zyjj1-R3
                                                                                        • +1
                                                                                          Но, кстати, приведённый wtf переводится на Python без потерь:
                                                                                          >>> numbers = [0,1,2,3,4]
                                                                                          >>> for number in numbers: number += 1
                                                                                          >>> numbers
                                                                                          [0, 1, 2, 3, 4]
                                                                                          • +2
                                                                                            Потому что в питоне (и в некоторых других) простые типы как строки и числа копируются, а словари, массивы и т.п. передаются ссылкой (т.е. копируется ссылка на объект).
                                                                                            • +1
                                                                                              Нет. Всё ссылка на объект, а вот += может быть вызовом метода __iadd__, а может быть присваиванием этой ссылке нового значения, если у объекта не оказалось этого метода.
                                                                                              • +2
                                                                                                Вот, кстати, доказательство:

                                                                                                   a = 5
                                                                                                => None
                                                                                                   b = a
                                                                                                => None
                                                                                                   object.__repr__(a)
                                                                                                => '<int object at 0x7fd04cc16700>'
                                                                                                   object.__repr__(b)
                                                                                                => '<int object at 0x7fd04cc16700>'
                                                                                                   b += 1
                                                                                                => None
                                                                                                   object.__repr__(b)
                                                                                                => '<int object at 0x7fd04cc16720>'
                                                                                                   b
                                                                                                => 6
                                                                                                   a
                                                                                                => 5
                                                                                                


                                                                                                PS извиняюсь за минус, не сразу понял о чем вообще речь
                                                                                                • +5
                                                                                                  Конкретно с мелкими int тут другая история. В классическом CPython целочисленные значения от -5 до 256 представляются ссылками на массив констант. Поэтому переменные с одинаковыми значениями в диапазоне [-5, 256] содержат одинаковые ссылки.
                                                                                                  >>> 256 is 256
                                                                                                  True
                                                                                                  >>> a = 256
                                                                                                  >>> b = 256
                                                                                                  >>> a is b
                                                                                                  True
                                                                                                  

                                                                                                  Для остальных целочисленных значений такое условие уже не выполняется:
                                                                                                  >>> 257 is 257
                                                                                                  True
                                                                                                  >>> a = 257
                                                                                                  >>> b = 257
                                                                                                  >>> a is b
                                                                                                  False
                                                                                                  

                                                                                                  Эта особенность поддерживается на уровне операций:
                                                                                                  >>> a = 256
                                                                                                  >>> b = 257
                                                                                                  >>> a is b
                                                                                                  False
                                                                                                  >>> b -= 1
                                                                                                  >>> a is b
                                                                                                  True
                                                                                                  
                                                                                                  • +3
                                                                                                    В примере mayorovp ничего не изменится, если вы замените a = 2 на a = 2.0. Данная оптимизация тут совершенно не причём: это иллюстрация к тому, что foo += bar есть foo = foo + bar, если у foo нет метода __iadd__ (в этом случае это будет foo.__iadd__(bar)). Ни у строк, ни у чисел в Python такого нет.
                                                                                                  • +2
                                                                                                    Если вам нужен адрес объекта, то вместо object.__repr__(obj) можно использовать id(obj): он быстрее печатается. Правда, не показывает тип, но здесь тип очевиден.
                                                                                          • +5
                                                                                            PHP.
                                                                                          • +4
                                                                                            в D программист управляет типом цикла(который может быть и по ссылке и по значению):
                                                                                            foreach(ref x; y) {

                                                                                            }
                                                                                            foreach(x; y) {

                                                                                            }
                                                                                            • +5
                                                                                              я тут на сцене появляется PHP как пример «совершенного» языка программирования, где это возможно :)
                                                                                              P.S. опоздал с комментом про PHP
                                                                                              • 0
                                                                                                Это можно сделать как минимум в C++ и D. Я уверен, что в доброй пачке других языков аналогичное поведение также возможно. Конкретно в данном случае автора смущает не то, нет циклов «по ссылке», а то, что это никак не освещенно в документации.
                                                                                                • +1
                                                                                                  Довольно странно освещать в документации отсутствие некоторой фичи…