HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Многие из нас привыкли считать, что действительно универсальных языков не бывает. Если нам нужна эффективность, мы используем "<strong>Си</strong>" и одновременно с этим миримся с его ограничениями.</p>
1 <p>Многие из нас привыкли считать, что действительно универсальных языков не бывает. Если нам нужна эффективность, мы используем "<strong>Си</strong>" и одновременно с этим миримся с его ограничениями.</p>
2 <p>Когда нам требуется повышенная скорость разработки, мы "дружим" с таким языком, как "<strong>Питон</strong>", ожидая получить медленный код.<strong>Erlang</strong>даёт нам возможность делать высокораспараллеленные распределённые приложения, однако его иногда бывает трудно вписать в существующие проекты.</p>
2 <p>Когда нам требуется повышенная скорость разработки, мы "дружим" с таким языком, как "<strong>Питон</strong>", ожидая получить медленный код.<strong>Erlang</strong>даёт нам возможность делать высокораспараллеленные распределённые приложения, однако его иногда бывает трудно вписать в существующие проекты.</p>
3 <p>Полностью ломает вышеописанную систему мышления, принятую на разных языках, именно<strong>Go</strong>. Он сочетает в себе<strong>преимущества множества языков</strong>, освобождая разработчиков от ряда их недостатков.</p>
3 <p>Полностью ломает вышеописанную систему мышления, принятую на разных языках, именно<strong>Go</strong>. Он сочетает в себе<strong>преимущества множества языков</strong>, освобождая разработчиков от ряда их недостатков.</p>
4 <h2>Язык Go - это "Си" на стероидах?</h2>
4 <h2>Язык Go - это "Си" на стероидах?</h2>
5 <p>Когда 10 лет тому назад<strong>Кена Томпсона</strong>, который активно участвовал в создании языка "Си", спросили, а каковым бы он сделал данный язык на тот момент, разработчик ответил, что он стал бы похож на<strong>Limbo</strong>. После этого прошло достаточно много времени, и Томпсон вместе с<strong>Робом Пайком</strong>(другой автора языка C) приняли участие в создании<strong>Go</strong>, ставшего в каком-то смысле переосмыслением и результатом развития Limbo. Таким образом, как только Go представили миру, он сразу<strong>стал бестселлером</strong>. И произошло это 10 ноября 2009 года.</p>
5 <p>Когда 10 лет тому назад<strong>Кена Томпсона</strong>, который активно участвовал в создании языка "Си", спросили, а каковым бы он сделал данный язык на тот момент, разработчик ответил, что он стал бы похож на<strong>Limbo</strong>. После этого прошло достаточно много времени, и Томпсон вместе с<strong>Робом Пайком</strong>(другой автора языка C) приняли участие в создании<strong>Go</strong>, ставшего в каком-то смысле переосмыслением и результатом развития Limbo. Таким образом, как только Go представили миру, он сразу<strong>стал бестселлером</strong>. И произошло это 10 ноября 2009 года.</p>
6 <p>Во многом успеху способствовали имена авторов, известных в качестве создателей операционной системы UNIX, кодировки UTF-8, языка "Си". Сыграло свою роль и покровительство<strong>Google</strong>, так как именно в лабораториях этой корпорации родился на свет Go.</p>
6 <p>Во многом успеху способствовали имена авторов, известных в качестве создателей операционной системы UNIX, кодировки UTF-8, языка "Си". Сыграло свою роль и покровительство<strong>Google</strong>, так как именно в лабораториях этой корпорации родился на свет Go.</p>
7 <p>Можно сказать, что старт был отличный. Однако одно это не позволило бы языку длительно держаться на плаву, если бы программистам не было предложено<strong>нечто действительно новое</strong>- нечто, упрощающее и жизнь, и разработку. И именно это "что-то" присутствовало в языке Go, имелось в большом количестве и сделало его в некоторых аспектах незаменимым.</p>
7 <p>Можно сказать, что старт был отличный. Однако одно это не позволило бы языку длительно держаться на плаву, если бы программистам не было предложено<strong>нечто действительно новое</strong>- нечто, упрощающее и жизнь, и разработку. И именно это "что-то" присутствовало в языке Go, имелось в большом количестве и сделало его в некоторых аспектах незаменимым.</p>
8 <h2>"Си" сегодняшнего дня</h2>
8 <h2>"Си" сегодняшнего дня</h2>
9 <p>Разработчики Go позиционируют его как системный язык, который сочетает в себе скорость исполнения и<strong>эффективность кода</strong>, написанного на C, с<strong>простотой разработки</strong>, присущей более высокоуровневым скриптовым языкам, включая языки, имеющие встроенные<strong>средства параллельного программирования</strong>. При этом если говорить о внешних признаках, то здесь Go напоминает нам некую странную солянку, состоящую из синтаксисов C, Pascal и ADA. В совокупности с приведенным выше описанием создаётся сильное ощущение некого подвоха (так бывает, если видишь новость о супер-мега-разработке группы студентов из Урюпинска). Но<strong>скепсис достаточно быстро исчезает</strong>, когда ты начинаешь изучать Go и понимаешь, почему он стал именно такой, какой есть сейчас.</p>
9 <p>Разработчики Go позиционируют его как системный язык, который сочетает в себе скорость исполнения и<strong>эффективность кода</strong>, написанного на C, с<strong>простотой разработки</strong>, присущей более высокоуровневым скриптовым языкам, включая языки, имеющие встроенные<strong>средства параллельного программирования</strong>. При этом если говорить о внешних признаках, то здесь Go напоминает нам некую странную солянку, состоящую из синтаксисов C, Pascal и ADA. В совокупности с приведенным выше описанием создаётся сильное ощущение некого подвоха (так бывает, если видишь новость о супер-мега-разработке группы студентов из Урюпинска). Но<strong>скепсис достаточно быстро исчезает</strong>, когда ты начинаешь изучать Go и понимаешь, почему он стал именно такой, какой есть сейчас.</p>
10 <p>В основе Go находятся<strong>3 фундаментальных принципа</strong>: 1. Гарантируется высокая скорость как компиляции, так и производительности приложений. 2. Обеспечивается простота разработки и технической поддержки приложений на уровне, присущем высокоуровневым скриптовым языкам. 3. Встроены средства параллельной разработки, которые позволяют использовать все ядра современных процессоров, которые имеются в железе.</p>
10 <p>В основе Go находятся<strong>3 фундаментальных принципа</strong>: 1. Гарантируется высокая скорость как компиляции, так и производительности приложений. 2. Обеспечивается простота разработки и технической поддержки приложений на уровне, присущем высокоуровневым скриптовым языкам. 3. Встроены средства параллельной разработки, которые позволяют использовать все ядра современных процессоров, которые имеются в железе.</p>
11 <p>Хотите знать, что всё это значит в реальности? Что же, давайте разберёмся по каждому пункту в отдельности.</p>
11 <p>Хотите знать, что всё это значит в реальности? Что же, давайте разберёмся по каждому пункту в отдельности.</p>
12 <h2>Производительность</h2>
12 <h2>Производительность</h2>
13 <p>Даже простейшая референсная реализация компилятора с языка Go может всего за доли секунды сгенерировать удивительно быстрый код, причём скорость его исполнения будет сопоставима со скоростью работы кода, написанного на "Си" или C++. Однако здесь следует добавить, что в отличие от своих именитых предков компилятор Go обеспечивает проверку типов, а результирующий код получает собственный механизм распараллеливания и встроенный сборщик мусора.</p>
13 <p>Даже простейшая референсная реализация компилятора с языка Go может всего за доли секунды сгенерировать удивительно быстрый код, причём скорость его исполнения будет сопоставима со скоростью работы кода, написанного на "Си" или C++. Однако здесь следует добавить, что в отличие от своих именитых предков компилятор Go обеспечивает проверку типов, а результирующий код получает собственный механизм распараллеливания и встроенный сборщик мусора.</p>
14 <p>С самых первых минут язык создавался так, чтобы<strong>быть максимально простым</strong>и понятным как человеку, так и машине. И многие архитектурные и синтаксические компоненты Go задумывались с оглядкой на то, чтобы сохранялась возможность их простого разбора программным обеспечением, будь то дебаггер, компилятор либо даже среда разработки.</p>
14 <p>С самых первых минут язык создавался так, чтобы<strong>быть максимально простым</strong>и понятным как человеку, так и машине. И многие архитектурные и синтаксические компоненты Go задумывались с оглядкой на то, чтобы сохранялась возможность их простого разбора программным обеспечением, будь то дебаггер, компилятор либо даже среда разработки.</p>
15 <p>В итоге язык получился достаточно<strong>прямолинейным</strong>, то есть не допускающим спорных мест и неочевидностей, которые могут затруднить работу компилятора (яркий пример неочевидного синтаксиса и сложной общей механики - тот же C++, который порой заставляет компилятор в буквальном смысле буксовать на месте, а головы разработчиков трещать от перенапряжения).</p>
15 <p>В итоге язык получился достаточно<strong>прямолинейным</strong>, то есть не допускающим спорных мест и неочевидностей, которые могут затруднить работу компилятора (яркий пример неочевидного синтаксиса и сложной общей механики - тот же C++, который порой заставляет компилятор в буквальном смысле буксовать на месте, а головы разработчиков трещать от перенапряжения).</p>
16 <p>Множество других элементов языка, которые не имеют прямого отношения к синтаксису, тоже были заранее<strong>оптимизированы</strong>. Например, язык не содержит механизма неявного приведения типов, а это оберегает программиста от ошибок, позволяя сделать проще компилятор. Также в языке отсутствует полноценная реализация классов с их полиморфизмом и наследованием. Что касается механизма параллельной разработки, то он задействует собственную реализацию потоков внутри любой программы - это делает потоки настолько лёгкими, что можно сказать, что их создание обходится почти даром. Весьма проворен и<strong>встроенный сборщик мусора</strong>- в языке попросту нет элементов, способных усложнить его работу. А в стандартную поставку Go включены плагины для любых популярных средств программирования, включая Vim.</p>
16 <p>Множество других элементов языка, которые не имеют прямого отношения к синтаксису, тоже были заранее<strong>оптимизированы</strong>. Например, язык не содержит механизма неявного приведения типов, а это оберегает программиста от ошибок, позволяя сделать проще компилятор. Также в языке отсутствует полноценная реализация классов с их полиморфизмом и наследованием. Что касается механизма параллельной разработки, то он задействует собственную реализацию потоков внутри любой программы - это делает потоки настолько лёгкими, что можно сказать, что их создание обходится почти даром. Весьма проворен и<strong>встроенный сборщик мусора</strong>- в языке попросту нет элементов, способных усложнить его работу. А в стандартную поставку Go включены плагины для любых популярных средств программирования, включая Vim.</p>
17 <h2>Простота в разработке и сопровождении</h2>
17 <h2>Простота в разработке и сопровождении</h2>
18 <p>Несмотря на то, что Go - системный язык, это не мешает ему быть довольно высокоуровневым, что необходимо для обеспечения программиста всем необходимым для быстрого и комфортного написания кода. Язык содержит ряд высокоуровневых конструкций, например, ассоциативные массивы и строки (их можно копировать, сравнивать, делать срезы, вычислять длину). Имеет он и средства для создания своих<strong>типов данных</strong>(подобны классам в других языках), плюс средства для создания потоков и обмена данными между ними. И, разумеется,<strong>он лишён указателей</strong>, которые способны ссылаться на любой участок памяти - в результате срыв стека в приложении, написанном на Go, в принципе невозможен.</p>
18 <p>Несмотря на то, что Go - системный язык, это не мешает ему быть довольно высокоуровневым, что необходимо для обеспечения программиста всем необходимым для быстрого и комфортного написания кода. Язык содержит ряд высокоуровневых конструкций, например, ассоциативные массивы и строки (их можно копировать, сравнивать, делать срезы, вычислять длину). Имеет он и средства для создания своих<strong>типов данных</strong>(подобны классам в других языках), плюс средства для создания потоков и обмена данными между ними. И, разумеется,<strong>он лишён указателей</strong>, которые способны ссылаться на любой участок памяти - в результате срыв стека в приложении, написанном на Go, в принципе невозможен.</p>
19 <p>Однако основное, что даёт Go разработчику, есть та самая очевидность синтаксиса и прямолинейность, о которой мы уже упоминали. В этом смысле Go напоминает такие языки, как Modula, Pascal и Oberon. И почти любой синтаксический элемент языка соответствует общей логике, поэтому может явно и безошибочно интерпретироваться вне зависимости от положения в коде. Например, вы просто не сможете сделать известную ошибку объявления переменных, которая описана во всех гайдах по стилю оформления кода на "Си":</p>
19 <p>Однако основное, что даёт Go разработчику, есть та самая очевидность синтаксиса и прямолинейность, о которой мы уже упоминали. В этом смысле Go напоминает такие языки, как Modula, Pascal и Oberon. И почти любой синтаксический элемент языка соответствует общей логике, поэтому может явно и безошибочно интерпретироваться вне зависимости от положения в коде. Например, вы просто не сможете сделать известную ошибку объявления переменных, которая описана во всех гайдах по стилю оформления кода на "Си":</p>
20 int* a, b; // В Си и C++ переменная "a" будет указателем, но "b" - нет var a, b *int; // В Go обе переменные будут указателями<p>В общем Go создан разработчиками для разработчиков. И проявляется данное обстоятельство во всём, начиная с обрамления блоков кода по аналогии со стилем "Си", неявного объявления типов, отсутствия необходимости проставлять точку с запятой после каждого выражения и заканчивая отсутствием механизма исключений и полноценных классов (создавались они, разумеется, для упрощения жизни, однако на практике часто становятся причиной запутывания кода).</p>
20 int* a, b; // В Си и C++ переменная "a" будет указателем, но "b" - нет var a, b *int; // В Go обе переменные будут указателями<p>В общем Go создан разработчиками для разработчиков. И проявляется данное обстоятельство во всём, начиная с обрамления блоков кода по аналогии со стилем "Си", неявного объявления типов, отсутствия необходимости проставлять точку с запятой после каждого выражения и заканчивая отсутствием механизма исключений и полноценных классов (создавались они, разумеется, для упрощения жизни, однако на практике часто становятся причиной запутывания кода).</p>
21 <p>И главная идея языка заключается в том, чтобы быть инструментом,<strong>позволяющим писать программы, а не постоянно думать о том, а будут ли они вообще работать</strong>(эта черта, как известно, присуща для таких языков, как "Си" и С++).</p>
21 <p>И главная идея языка заключается в том, чтобы быть инструментом,<strong>позволяющим писать программы, а не постоянно думать о том, а будут ли они вообще работать</strong>(эта черта, как известно, присуща для таких языков, как "Си" и С++).</p>
22 <h2>Средства параллельного программирования</h2>
22 <h2>Средства параллельного программирования</h2>
23 <p>Это одна из самых сильных черт Go. Пожалуй, среди языков общего назначения "голангу" просто нет равных (кроме, разве что, Limbo, однако он привязан к операционной системе Inferno).</p>
23 <p>Это одна из самых сильных черт Go. Пожалуй, среди языков общего назначения "голангу" просто нет равных (кроме, разве что, Limbo, однако он привязан к операционной системе Inferno).</p>
24 <p>Выигрыш тут состоит не только в том, что средства встроены. Намного большее значение имеет факт, что эти средства реализуют простую и эффективную модель, которая полностью соответствует<strong>CSP</strong>- теории взаимодействующих последовательных процессов. Те, кто знаком с Occam и Limbo, хорошо понимают все плюсы CSP, а кто не знаком, сейчас поясним. Смотрите, вместо того, чтобы нагромождать потоки, мьютексы, блокировки и прочие системы синхронизации, делающие параллельное программирование настоящей мукой и приводящие и переизданию многостраничных томов о том, как правильно писать многопоточные приложения, у нас существует<strong>CSP</strong>. Его автор,<strong>Тони Хоар</strong>, предлагает элегантное и довольно простое решение:<strong>позволить приложению в любое время создать новую нить</strong>, которая будет иметь возможность общаться с родителями и прочими нитями посредством отправки синхронных сообщений.</p>
24 <p>Выигрыш тут состоит не только в том, что средства встроены. Намного большее значение имеет факт, что эти средства реализуют простую и эффективную модель, которая полностью соответствует<strong>CSP</strong>- теории взаимодействующих последовательных процессов. Те, кто знаком с Occam и Limbo, хорошо понимают все плюсы CSP, а кто не знаком, сейчас поясним. Смотрите, вместо того, чтобы нагромождать потоки, мьютексы, блокировки и прочие системы синхронизации, делающие параллельное программирование настоящей мукой и приводящие и переизданию многостраничных томов о том, как правильно писать многопоточные приложения, у нас существует<strong>CSP</strong>. Его автор,<strong>Тони Хоар</strong>, предлагает элегантное и довольно простое решение:<strong>позволить приложению в любое время создать новую нить</strong>, которая будет иметь возможность общаться с родителями и прочими нитями посредством отправки синхронных сообщений.</p>
25 <p>Если говорить о Go, то в данном случае эта идея выглядит следующим образом: 1. Создаётся переменная-канал. 2. Определяется функция, принимающая переменную-канал в виде аргумента и в своём теле содержащая код, который выполняется в отдельной нити. В конце эта функция отправляет результат выполнения в канал, что делается благодаря специальному оператору. 3. Функция запускается в отдельном потоке посредством ключевого слова "<strong>go</strong>". 4. Выполняется чтение из канала.</p>
25 <p>Если говорить о Go, то в данном случае эта идея выглядит следующим образом: 1. Создаётся переменная-канал. 2. Определяется функция, принимающая переменную-канал в виде аргумента и в своём теле содержащая код, который выполняется в отдельной нити. В конце эта функция отправляет результат выполнения в канал, что делается благодаря специальному оператору. 3. Функция запускается в отдельном потоке посредством ключевого слова "<strong>go</strong>". 4. Выполняется чтение из канала.</p>
26 <p>То есть происходит ответвление функции от основного потока исполнения, который в то же самое время переходит к ожиданию данных в канале. Результат исполнения поступает в канал, где основной поток его получает. Звучит довольно просто, но давайте посмотрим, как это выглядит в коде.</p>
26 <p>То есть происходит ответвление функции от основного потока исполнения, который в то же самое время переходит к ожиданию данных в канале. Результат исполнения поступает в канал, где основной поток его получает. Звучит довольно просто, но давайте посмотрим, как это выглядит в коде.</p>
27 <h2>Пример</h2>
27 <h2>Пример</h2>
28 <p>Один из наиболее популярных примеров, который хорошо демонстрирует мощь Go, -<strong>реализация таймера</strong>. Он выполняется в отдельном потоке и подаёт сигналы основному потоку через некоторые определённые промежутки времени, в течение которых переходит в спящий режим. Если писать код данной программы на любом из классических языков программирования, он выглядел бы довольно запутанным и громоздким. Однако в Go он выглядит просто и красиво:</p>
28 <p>Один из наиболее популярных примеров, который хорошо демонстрирует мощь Go, -<strong>реализация таймера</strong>. Он выполняется в отдельном потоке и подаёт сигналы основному потоку через некоторые определённые промежутки времени, в течение которых переходит в спящий режим. Если писать код данной программы на любом из классических языков программирования, он выглядел бы довольно запутанным и громоздким. Однако в Go он выглядит просто и красиво:</p>
29 package main import "time" import "fmt" func timer(ch chan string, ns, count int) { for j := 1; j &lt;= count; j++ { time.Sleep(int64(ns)) if j == count { fmt.Printf("[timer] Отправляю последнее сообщение...n") ch &lt;- "стоп!" } else { fmt.Printf("[timer] Отправляю...n") ch &lt;- "продолжаем" } fmt.Printf("[timer] Отправил!n") } } func main() { var str string ch := make(chan string) go timer(ch, 1000000000, 10) for { fmt.Printf("[main] Принимаю...n") str = &lt;-ch if str == "стоп!" { fmt.Printf("[main] Принял последнее сообщение, завершаю работу.n") return } else { fmt.Printf("[main] Принято!n") } } }<p>Если бы мы выбрали простейшую реализацию данной программы, то она заняла бы и вовсе 15 строк. Но наш код<strong>намеренно усложнён</strong>путём добавления условных выражений и вывода на терминал. Они необходимы для понимания общего синтаксиса языка Go, а также механизма работы планировщика потоков.</p>
29 package main import "time" import "fmt" func timer(ch chan string, ns, count int) { for j := 1; j &lt;= count; j++ { time.Sleep(int64(ns)) if j == count { fmt.Printf("[timer] Отправляю последнее сообщение...n") ch &lt;- "стоп!" } else { fmt.Printf("[timer] Отправляю...n") ch &lt;- "продолжаем" } fmt.Printf("[timer] Отправил!n") } } func main() { var str string ch := make(chan string) go timer(ch, 1000000000, 10) for { fmt.Printf("[main] Принимаю...n") str = &lt;-ch if str == "стоп!" { fmt.Printf("[main] Принял последнее сообщение, завершаю работу.n") return } else { fmt.Printf("[main] Принято!n") } } }<p>Если бы мы выбрали простейшую реализацию данной программы, то она заняла бы и вовсе 15 строк. Но наш код<strong>намеренно усложнён</strong>путём добавления условных выражений и вывода на терминал. Они необходимы для понимания общего синтаксиса языка Go, а также механизма работы планировщика потоков.</p>
30  
30