Go — один из тех язы­ков, что не оставля­ют никого рав­нодуш­ным: его или любят, или хей­тят. Он необы­чен — син­таксис чем‑то напоми­нает C, Kotlin и Pascal одновре­мен­но. Он кра­ток — мало клю­чевых слов, нет ООП, нет исклю­чений и даже перечис­лений. Но при этом в Go мож­но все­го парой строк под­нять веб‑сер­вер, не ска­чивая ни одно­го нового пакета.

Этот матери­ал откры­вает цикл ста­тей, где я покажу воз­можнос­ти и осо­бен­ности это­го язы­ка на живых при­мерах. Это не сов­сем руководс­тво в духе «учим­ся прог­рамми­ровать с пол­ного нуля»: в начале я пос­тара­юсь понят­но рас­ска­зать о самых осно­вах, что­бы ты смог написать и понять прос­тей­шую прог­рамму, но даль­ше раз­бор слож­ных вещей на дет­садов­ском уров­не был бы слиш­ком объ­емным. Поэто­му пос­тепен­но перей­дем на взрос­лые объ­ясне­ния для тех, кто хоть нем­ного видел дру­гие язы­ки прог­рамми­рова­ния. Как далеко смо­гут про­тянуть нович­ки — не уве­рен. Делись впе­чат­лени­ями в ком­мента­риях!

При­меры будут по воз­можнос­ти перек­ликать­ся с реаль­ными задача­ми. Одна­ко, разуме­ется, эти при­меры в чем‑то син­тетичес­кие и во мно­гом упро­щен­ные. Мы не будем писать вто­рой Metasploit с нуля, да и не смо­жем это­го сде­лать.

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

Моя основная цель — показать сов­ремен­ный и необыч­ный язык, который час­то (и незас­лужен­но) обхо­дят вни­мани­ем, счи­тая нишевым или экс­тра­ваган­тным.

info

У Go есть и аль­тер­натив­ное наз­вание — Golang. Из‑за рас­простра­нен­ности гла­гола to go в англий­ском язы­ке поис­ковые зап­росы со сло­вом go зачас­тую при­водят сов­сем не туда, куда хотелось бы. Поэто­му, ког­да гуг­лишь что‑то, луч­ше исполь­зовать сло­во Golang.

Да­леко ходить не ста­нем и нач­нем с клас­сичес­кого Hello, world:

package main
import "fmt"
func main() {
fmt.Println("Привет, Хакер!")
}

www

Все исходни­ки ты можешь най­ти в моем ре­пози­тории.

Да­вай раз­берем пос­троч­но. Сна­чала объ­явля­ем пакет с име­нем main. Па­кет в Go — это каталог с фай­лами, где каж­дый файл начина­ется со сло­ва package и наз­вания пакета.

Мы написа­ли, что наш код вклю­чен в пакет main: если ком­пилятор най­дет в том же катало­ге дру­гие фай­лы с таким началом, то все их объ­еди­нит. Нуж­но это, что­бы мож­но было раз­бить длин­ный код на отдель­ные смыс­ловые час­ти, но обра­щать­ся к перемен­ным и фун­кци­ям, задан­ным в дру­гих час­тях (в дру­гих язы­ках такой механизм называ­ется прос­транс­твом имен — namespace).

Сле­дующей строч­кой мы импорти­руем пакет fmt, содер­жащий фун­кции кон­соль­ного вво­да и вывода. Для его импорта, как и дру­гих пакетов из стан­дар­тной биб­лиоте­ки, дос­таточ­но ука­зать толь­ко имя. Для пакетов сто­рон­них раз­работ­чиков пот­ребу­ется ука­зать источник, нап­ример github.com/spf13/viper, потому что в Go нет цен­траль­ного репози­тория пакетов (при этом есть общий ресурс с до­кумен­таци­ей по пакетам).

На­конец, точ­ка вхо­да в при­ложе­ние — фун­кция main, не при­нима­ющая аргу­мен­тов и не воз­вра­щающая зна­чений.

Объ­явле­ние любой фун­кции в Go начина­ется клю­чевым сло­вом func. Даль­ше идет наз­вание фун­кции (main), пус­тые скоб­ки (здесь могут быть аргу­мен­ты, которые при­нима­ет фун­кция, но main у нас ничего не при­нима­ет) и в фигур­ных скоб­ках — тело фун­кции.

Важ­ный момент: откры­вающая фигур­ная скоб­ка никог­да не дол­жна сто­ять в новой стро­ке. В про­тив­ном слу­чае утвер­жде­ние будет обра­бота­но неп­равиль­но и воз­никнет ошиб­ка ком­пиляции.

info

В C мы написа­ли бы void main(void), а в Go прос­то не ука­зыва­ем ничего.

Внут­ри main мы вызыва­ем фун­кцию Println из ранее импорти­рован­ного пакета fmt: сна­чала fmt, затем точ­ка, затем Println и в скоб­ках (как ты уже зна­ешь, в них идет аргу­мент) — стро­ка «При­вет, Хакер!», которую мы хотим вывес­ти в кон­соль. Стро­ки, как и в боль­шинс­тве язы­ков, обо­рачи­вают­ся в двой­ные кавыч­ки.

www

Я здесь не буду опи­сывать син­таксис офор­мле­ния вывода и аль­тер­натив­ные вари­анты фун­кции — под­робнос­ти ты най­дешь в до­ках самос­тоятель­но.

info

По­ка что по син­такси­су все при­выч­но для тех, кто нем­ного зна­ком с любым дру­гим C-подоб­ным язы­ком. Но даль­ше встре­тят­ся некото­рые осо­бен­ности. Нап­ример, Go тоже исполь­зует точ­ку с запятой как мар­кер окон­чания утвер­жде­ния. Но, сле­дуя парадиг­ме минима­лис­тичнос­ти, Go не тре­бует от прог­раммис­та ста­вить точ­ки с запятой и дела­ет это сам. Отсю­да и зап­рет на фигур­ные скоб­ки в отры­ве от объ­явле­ния фун­кции — ина­че меж­ду ними вста­вилась бы точ­ка с запятой.

История и особенности языка Go

Go был раз­работан внут­ри ком­пании Google как собс­твен­ная аль­тер­натива C++ для при­мене­ния в высоко­наг­ружен­ных сер­висах. Раз­работ­чики — «звез­дная коман­да»: Роберт Гриз­мер (бра­узер­ный дви­жок Google V8), Роб Пайк (Unix), Кен Том­псон (Unix, C). Пер­вая вер­сия пред­став­лена в 2009 году. Основная цель соз­дания язы­ка — всес­торон­не упростить и облегчить C++, не теряя в воз­можнос­тях и ско­рос­ти исполне­ния.

Глав­ная осо­бен­ность Go — лаконич­ность. В нем мало слов и понятий, порой даже слиш­ком (так, помимо нас­ледова­ния и пере­опре­деле­ния фун­кций, в язык не вклю­чили и ternary operator, и даже enum). С дру­гой сто­роны, в наличии авто­мати­чес­кое управле­ние памятью и неверо­ятно обширная стан­дар­тная биб­лиоте­ка.

Те­бе не при­дет­ся, как в C, на каж­дый чих изоб­ретать свой велоси­пед из сотен строк — к тво­им услу­гам куча воз­можнос­тей, от пар­синга JSON до ZIP-архи­виро­вания. При этом сох­раня­ется прос­тота и ком­пак­тность. Никаких гигабай­тных SDK и тан­цев с вер­сиями сотен зависи­мос­тей в пакет­ном менед­жере.

Сре­ди силь­ных сто­рон Go обыч­но называ­ют сле­дующее:

  • прос­тота во всем — от изу­чения клю­чевых слов до поис­ка и под­клю­чения пакетов;
  • лег­кость пере­исполь­зования кода;
  • вы­сокая ско­рость ком­пиляции;
  • ком­пак­тный тул­чейн;
  • кросс‑ком­пиляция под мно­жес­тво плат­форм и ста­тичес­кая лин­ковка бинар­ников, исклю­чающая необ­ходимость тас­кать за собой зависи­мос­ти;
  • раз­витая сис­тема мно­гопо­точ­ности с собс­твен­ным пла­ниров­щиком, уско­ряющим работу и сни­жающим наг­рузку на ОС.

Ты можешь воз­разить: раз­ве мно­гопо­точ­ность так уж ред­ка в сов­ремен­ных язы­ках прог­рамми­рова­ния? И будешь, конеч­но, прав. Но дело здесь в прос­тоте и пороге вхож­дения. Тол­щина кни­жек «Java Concurrency на прак­тике» пуга­ет джу­нов, а о под­водные кам­ни пери­оди­чес­ки спо­тыка­ются даже сень­оры. Тем вре­менем Go изна­чаль­но соз­давал­ся как мно­гопо­точ­ный язык, с мак­сималь­ной пря­моли­ней­ной и оче­вид­ной реали­заци­ей это­го механиз­ма.

На Go или с его исполь­зовани­ем написа­ны такие извес­тные инс­тру­мен­ты, как Docker, Kubernetes, Terraform, Prometheus, Grafana. Для соз­дания собс­твен­ных сер­висов Go исполь­зуют в Google, Uber, Netflix, Dropbox, Twitch и мно­жес­тве дру­гих ком­паний.

На­конец, если тебе инте­рес­но, как реали­зова­на та или иная фича язы­ка, с Go ты без тру­да можешь заг­лянуть «под капот» — и сам язык, и стан­дар­тная биб­лиоте­ка рас­простра­няют­ся по сво­бод­ной лицен­зии, как и подав­ляющее боль­шинс­тво пакетов, соз­данных сто­рон­ними раз­работ­чиками.

Та­лис­ман язы­ка Go — синий муль­тяш­ный сус­лик (англ. gopher).

 

Инструментарий

Да­вай раз­берем­ся, как вооб­ще соз­давать и запус­кать прог­раммы. И здесь Go про­дол­жает сле­довать парадиг­ме минима­лис­тичнос­ти. Для раз­работ­ки прог­рамм тебе пот­ребу­ется единс­твен­ный ин­стал­ляци­онный пакет под твою плат­форму, раз­мером где‑то 60 Мбайт. Там есть все, что нуж­но для сбор­ки прог­рамм под любую под­держи­ваемую ОС и архи­тек­туру.

Я пока не говорил? Кросс‑плат­формен­ность — еще одна фиш­ка Go, вари­аций под­держи­ваемых плат­форм мно­жес­тво, от Windows до Solaris, от 386 до MIPS и POWER8. Все вер­но, для любой из них ты можешь соб­рать бинар­ник на сво­ем компь­юте­ре без каких‑либо допол­нитель­ных инс­тру­мен­тов и зависи­мос­тей.

Код мож­но писать хоть в блок­ноте или nano, но обыч­но исполь­зуют более богатые воз­можнос­тями сре­ды: VS Code или, нап­ример, Zed. Есть и ком­мерчес­кая IDE GoLand.

www

По неиз­вес­тной мне при­чине в популяр­ный Notepad++ встро­енную под­свет­ку син­такси­са Go до сих пор не завез­ли. Выруча­ют поль­зователь­ские сти­ли, вот при­мер.

Пос­ле уста­нов­ки мы можем вос­поль­зовать­ся кон­соль­ной ути­литой go.

В Linux:

mkdir hello && cd $_
touch hello.go && nano $_
# Скопипасти текст примера выше
go run hello.go

В Windows:

mkdir hello
cd hello
notepad hello.go
:: Скопипасти текст примера выше
go run hello.go

Ко­ман­да go run собира­ет бинар­ник из ука­зан­ного фай­ла, помеща­ет его во вре­мен­ный каталог и запус­кает, а пос­ле завер­шения работы — уда­ляет. Если же нуж­но сох­ранить соб­ранный код, исполь­зуй go build (c клю­чом -o мож­но ука­зать жела­емое имя выход­ного фай­ла).

В целом это­го дос­таточ­но, что­бы писать и выпол­нять прос­тые прог­раммы, исполь­зующие толь­ко средс­тва стан­дар­тной биб­лиоте­ки.

Кросс-компиляция

Цель сбор­ки зада­ется дву­мя перемен­ными окру­жения: GOOS опре­деля­ет опе­раци­онную сис­тему и GOARCH — архи­тек­туру. Воз­можные ком­бинации мож­но уви­деть, выпол­нив коман­ду go tool dist list.

К при­меру, можем в Windows соб­рать бинар­ник для Linux ARM64 (нап­ример, Raspberry Pi):

set GOOS=linux&& set GOARCH=arm64&& go build -o hello_arm64

Об­рати вни­мание на отсутс­твие про­белов пос­ле зна­чений перемен­ных.

Ес­ли же нуж­но из Linux соб­рать .exe для Windows, можем задать такие парамет­ры:

GOOS=windows GOARCH=amd64 go build -o hello.exe
Сборка для разных платформ
Сбор­ка для раз­ных плат­форм

Преж­де чем пой­ти даль­ше, поз­накомим­ся еще с одной важ­ной кон­цепци­ей — модуля­ми. Мо­дулем называ­ют один или нес­коль­ко пакетов, сос­тавля­ющих еди­ное целое. Как пра­вило, модуль — это отдель­ное при­ложе­ние, и в таком слу­чае он обя­затель­но дол­жен содер­жать один и толь­ко один пакет main, в котором находит­ся одна фун­кция main — она будет точ­кой вхо­да в при­ложе­ние.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    4 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии