Що написано на go. Програмування на Go. Основи мови Go

Останнє оновлення: 13.05.2019

Go являє компільований статично типізований мова програмування від компанії Google. Мова Go призначений для створення різного роду додатків, але перш за все це веб-сервіси і клієнт-серверні додатки. Хоча також мову має можливості по роботі з графікою, низькорівневими можливостями і т.д.

Робота над мовою Go почалася в 2007 в надрах компанії Google. Одним з авторів є Кен Томпсон, який, до слова, є і одним з авторів мови Сі (поряд з Денисом Рітчі). 10 листопада 2009 року мову був анонсований, а в березні 2012 року вийшла версія 1.0. При цьому мова продовжує розвиватися. Поточною версією на момент написання даної статті є версія 1.12, яка вийшла в лютому 2019 року.

Мова Go розвивається як open source, тобто представляє поект з відкритим вихідним кодом, і все його коди і компілятор можна знайти і використовувати безкоштовно. Офіційний сайт проекту - https://golang.org, де можна багато корисної інформації про мову.

Go є кросплатформним, він дозволяє створювати програми під різні операційні системи - Windows, Mac OS, Linux, FreeBSD. Код має переносимість: програми, написані для однієї з цих операційних систем, можуть бути легко з перекомпиляцией перенесені на іншу ОС.

Основні особливості мови Go:

    компільований - компілятор транслює програму на Go в машинний код, зрозумілий для певної платформи

    статично типізований

    присутній збирач сміття, який автоматично очищає пам'ять

    підтримка роботи з мережевими протоколами

    підтримка багатопоточності і паралельного програмування

В даний час Go знаходить широке застосування в різних сферах. Зокрема, серед відомих проектів, які застосовують Go, можна знайти такі: Google, Dropbox, Netflix, Kubernetes, Docker, Twitch, Uber, CloudFlare і ряд інших.

Що потрібно для роботи з Go? Перш за все необхідний текстовий редактор для набору коду і компілятор для перетворення коду в виконуваний файл. Також можна використовувати спеціальні інтегровані середовища розробки (IDE), які підтримують Go, наприклад, GoLand від компанії JetBrains. Існують плагіни для Go для інших IDE, зокрема, IntelliJ IDEA і Netbeans.

установка компілятора

Пакет для установки компілятора можна завантажити з офіційного сайту https://golang.org/dl/.

За цією адресою пакети установників для різних операційних систем. Так, під час запуску і запуску установника для Windows відкривається наступне вікно:

Після прийняття ліцензійної угоди відобразиться вікно для вибору місця установки:

За замовчуванням використовується шлях "c: \\ go". Залишимо цей шлях за замовчуванням і перейдемо до наступного вікна, на якому натиснемо на кнопку Install:

Після успішної установки в папці C: \\ Go будуть встановлені всі файли, необхідні для роботи з Go. Зокрема, в папці C: \\ Go \\ bin можна знайти файл go.exe, який виконує роль компілятора.

Ми звикли думати, що по-справжньому універсальних мов програмування не існує. Коли нам потрібна ефективність - ми пишемо на Сі і миримося з його обмеженнями. Коли потрібна швидкість розробки - Кодима на Python і очікуємо отримати повільний код. Erlang дозволяє створювати високораспараллеленние розподілені додатки, але його дуже важко вписати в існуючі проекти. Мова Go повністю ламає таку систему мислення, поєднуючи в собі переваги багатьох мов і звільняючи програмістів від їх недоліків.

Коли десять років тому Кена Томпсона, який брав активну участь в розробці мови Сі, запитали, яким би він зробив цю мову на той момент, він відповів, що мова була б схожий на Limbo. Минуло чимало часу, і Томпсон спільно з ще одним автором мови Сі, Робом Пайком, взяв участь у створенні Go - мови, який став переосмисленням і подальшим розвитком Limbo. Go був представлений світу 10 листопада 2009 року та практично відразу став бестселером. Лише імена авторів, відомих як творці операційної системи UNIX, мови програмування Сі та кодування UTF-8, а також заступництво Google, в лабораторіях яких була створена мова, дали Go відмінний старт. Однак навіть це не дозволило б мови довго протриматися на плаву, якщо б він не зміг запропонувати програмістам щось дійсно нове - щось, що спростило б їхнє життя і зробило Go по-справжньому незамінним. І це "щось" в мові було. У Великій кількості.

Сі сьогоднішнього дня

Творці Go позиціонують своє дітище як системний мову, що поєднує в собі ефективність і швидкість виконання коду, написаного на Сі, з простотою розробки на більш високорівневих скриптових мовах, та ще й з вбудованими засобами паралельного програмування. При цьому зовні Go нагадує якусь дивну солянку з синтаксисів мов Сі, Pascal і ADA, що укупі з наведеним описом створює досить сильне відчуття підступу, майже таке ж, яке виникає, коли чуєш про нову мега-розробці пятигорских студентів. Однак воно швидко убуває, коли ти починаєш вивчати мову, і зовсім зникає, коли дізнаєшся про те, чому Go став саме таким, яким він є.

В основу Go належить три фундаментальних ідеї:

  1. Гарантія високої швидкості компіляції та продуктивності додатків.
  2. Простота розробки і підтримки додатків, властива високорівневим скриптовою мов.
  3. Засоби паралельного програмування, що дозволяють задіяти всі наявні ядра сучасних процесорів.

Що все це означає на ділі? Розберемося з кожним з пунктів.

продуктивність

Навіть дуже проста референсна реалізація компілятора з мови Go здатна за якісь частки секунди згенерувати на подив швидкий код, швидкість виконання якого буде порівнянна зі швидкістю виконання коду, написаного на таких мовах, як Сі і C ++. При цьому, на відміну від своїх предків, компілятор Go гарантує перевірку типів, а результуючий код отримує вбудований збирач сміття і власний механізм розпаралелювання.

З самого початку мова проектувався таким чином, щоб бути легко зрозумілим і простим в "перетравленні" не тільки людині, але і машині. Багато синтаксичні та архітектурні елементи Go були задумані якщо і не з головною метою, то, по крайней мере, з оглядкою на можливість їх простого розбору програмою, будь то компілятор, дебагер або навіть середовище розробки. Мова вийшов дуже прямолінійним і недопускающим неочевидним і спірних місць, які могли б привести компілятор в замішання (мова C ++ - яскравий приклад такого неочевидного синтаксису і загальної механіки, які змушують голови програмістів тріщати, а компілятор - повільно буксувати на місці).

Багато інших елементів мови, що не мають прямого відношення до синтаксису, також були оптимізовані заздалегідь. Наприклад, мова не має механізму неявного приведення типів, що захищає програміста від помилок і дозволяє зробити компілятор простіше. У мові немає повноцінної реалізації класів з їх спадкуванням та поліморфізмом. Механізм паралельного програмування використовує власну реалізацію потоків всередині кожної програми, що робить потоки настільки легкими, що їх створення обходиться практично задарма. Вбудований збирач сміття також вельми спритний, в мові просто немає елементів, які могли б ускладнити його роботу.

У стандартну поставку Go входять плагіни для всіх популярних середовищ програмування, в тому числі Vim

Простота розробки і супроводу

Go - системний мову, що, тим не менш, не заважає йому бути досить високорівневих для того, щоб забезпечити програміста всім необхідним для комфортного і швидкого написання коду. Мова включає в себе такі високорівневі конструкції, як асоціативні масиви і рядки (які можна порівнювати, копіювати, обчислювати довжину, робити зрізи). Він має засоби для створення власних типів даних (подібних класів в інших мовах), засоби створення потоків і обміну даними між ними, і, звичайно ж, він позбавлений покажчиків, здатних посилатися на будь-яку ділянку пам'яті (зрив стека в програмі, написаній на Go, неможливий в принципі). Однак головне, що дає Go програмісту, це та сама прямолінійність і очевидність синтаксису, про яку ми говорили в попередньому розділі. У цьому сенсі Go дуже схожий на мови Pascal, Modula і Oberon: практично будь-який синтаксичний елемент мови слід загальній логіці і може бути явно і безпомилково інтерпретований незалежно від його положення в коді. Наприклад, зробити знамениту помилку оголошення змінних, описану в усіх гайдах по стилістиці оформлення коду на мові Сі, в Go просто неможливо:

int * a, b; // У Сі і C ++ змінна "a" буде дороговказом, але "b" - немає
var a, b * int; // В Go обидві змінні будуть покажчиками

Go - мова, створена програмістами і для програмістів. Це проявляється у всьому, починаючи від обрамлення блоків коду в стилі Сі, неявного оголошення типів, відсутності необхідності ставити крапку з комою після кожного виразу і закінчуючи такими архітектурними рішеннями, як відсутність механізму виключень і повноцінних класів (вони були створені для спрощення життя, але замість цього призводять до заплутування коду). Основна ідея мови в тому, щоб бути інструментом, який дозволяє писати програми, замість того, щоб думати про те, зароблять вони взагалі (ця риса властива Сі і, в ще більшому ступені, C ++).

Засоби паралельного програмування

Засоби паралельного програмування - це найсильніша риса Go, і тут серед мов загального призначення йому просто немає рівних (за винятком хіба що Limbo, але він прив'язаний до ОС Inferno). І виграш тут не стільки в тому, що ці кошти вбудовані в саму мову, скільки в тому, що вони реалізують дуже просту і ефективну модель, повністю наступну теорії взаємодіючих послідовних процесів (CSP). Читачі, знайомі з Occam і Limbo, повинні добре розуміти всі переваги CSP, а для інших поясню. Замість того, щоб городити город з потоків, блокувань, м'ютексів і інших систем синхронізації, які роблять паралельне програмування нестерпною мукою і призводять до видання багатосторінкових книжок про те, як писати багатопотокові програми, автор CSP Тоні Хоар пропонує просте і елегантне рішення: дозволити додатком в будь-який момент створити нову гілку, яка зможе спілкуватися з батьком і іншими нитками за допомогою відправки синхронних повідомлень.

У Go ця ідея виглядає так:

  1. Створення змінної-каналу.
  2. Визначення функції, яка приймає змінну-канал в якості аргументу, а в своєму тілі містить код, який повинен бути виконаний в окремій нитки. В кінці функція повинна відправити результат свого виконання в канал (це робиться за допомогою спеціального оператора).
  3. Запуск функції в окремому потоці за допомогою ключового слова "go".
  4. Читання з каналу.

Функція відгалужується від основного потоку виконання, який в цей час переходить до очікування даних в каналі, результат виконання функції відправляється в канал і основний потік отримує його. Просто, чи не так? Але як це буде виглядати в коді?

приклад

Один з моїх улюблених прикладів, які демонструють міць мови Go, - це реалізація таймера, який виконується в окремому потоці і "стукає" основного потоку через певні інтервали часу, протягом яких йде в сон. Код цієї програми, написаний на одному з "класичних" мов програмування, виглядав би громіздким і заплутаним, але Go дозволяє зробити його простим і красивим.

Код нашої програми:

1 package main
2
3 import "time"
4 import "fmt"
5
6 func timer (ch chan string, ns, count int) (
7 for j: \u003d 1; j<= count; j++ {
8 time.Sleep (int64 (ns))
9 if j \u003d\u003d count (
10 fmt.Printf ( "Відправляю останні повідомлення ... n")
11 ch<- "стоп!"
12) else (
13 fmt.Printf ( "Відправляю ... n")
14 ch<- "продолжаем"
15 }
16 fmt.Printf ( "Відправив! N")
17 }
18 }
19
20 func main () (
21 var str string
22
23 ch: \u003d make (chan string)
24 go timer (ch, 1000000000, 10)
25
26 for (
27 fmt.Printf ( "Приймаю ... n")
28 str \u003d<-ch
29 if str \u003d\u003d "стоп!" (
30 fmt.Printf ( "Прийняв останні повідомлення, завершую работу.n")
31 return
32) else (
33 fmt.Printf ( "Прийнято! N")
34 }
35 }
36 }

Найпростіша реалізація цієї програми зайняла б п'ятнадцять рядків, але я навмисно ускладнив її, додавши висновок на термінал і умовні вирази. Вони допоможуть зрозуміти загальний синтаксис мови і механізм роботи планувальника потоків Go. Висновок команди наведено на скріншоті.

Результат роботи програми після п'яти ітерацій циклу

На перший погляд лістинг дуже нагадує код програми, написаної на мові Сі, C ++ або навіть Java, але при більш детальному вивченні стають видні відмінності - Go успадкував від Сі тільки базовий синтаксис, в той час як більшість ключових слів і лексика змінилися. Вихідний код починається з ключового слова package, слідом за яким йде ім'я пакета, до якого цей код відноситься. Все що запускаються користувачем програми повинні мати ім'я main, тоді як бібліотеки можуть мати довільне ім'я, яке буде використано для доступу до її функцій і змінним після імпортування. При цьому для позначки, чи повинна функція або змінна бути експортованої, використовується верхній регістр: всі об'єкти, імена яких починаються з великої літери, будуть експортовані, інші залишаться приватними.

У рядках 3 і 4 відбувається імпортування пакетів time і fmt, функції яких знадобляться нам пізніше. Імпорт пакетів багато в чому дуже схоже на включення в програму заголовних файлів, як це робиться в Сі і C ++, з тим винятком, що Go, по-перше, стежить за простором імен і всі імпортовані функції, змінні і типи даних будуть мати префікс у вигляді імені пакета, а по-друге, не вимагає наявності самих заголовків файлів. Ніякої метушні з Хідер і простором імен!

З рядка 6 починається опис функції timer () нашого головної дійової особи. В подальшому коді вона буде відправлена \u200b\u200bв окремий потік, і більшу частину часу проведе уві сні, а прокидаючись, звітуватиме головному потоку. Щоб зробити це, їй потрібен доступ до каналу, тому перший аргумент функції - це ch типу "канал для передачі рядків". Також їй потрібно знати часовий відрізок, протягом якого вона може спати, і то, скільки разів вона зможе це зробити. Тому другий і третій аргументи - це ns і count типу int. Зверни увагу на форму опису аргументів. На відміну від Сі, в Go спочатку йде ім'я змінної, і лише після - її тип (що логічно і узгоджується з системою мислення людини: "змінна така-то такого-то типу"). Тип, що повертається функцією значення в Go слід поміщати в кінець, відразу за закриваючою дужкою (що, до речі, теж логічно). При цьому, якщо функція повинна повертати кілька значень (в Go це можливо), їх типи і (опціонально) імена повинні бути перераховані через кому і обрамлені дужками. У нашій функції повертається немає - пішовши в окремий потік, вона так чи інакше нічого повернути не зможе. Функція повинна повторити процедуру "сон - звіт" вказане в змінної count число раз, тому в рядку 7 починається цикл for, запис якого повністю аналогічна своєму побратиму в мові Сі, за винятком відсутності дужок.

Щоб відправити потік timer в сон ми використовуємо функцію Sleep (рядок 8) з раніше імпортованого пакету time. Її аргумент, що задає тривалість сну, повинен мати тип int64 (аналогічний типу long в Сі), тому ми повинні використовувати приведення типів, компілятор не зробить цього за нас (і правильно, ми розумніші). Щоб головний потік знав, коли потік timer завершиться, і зміг обробити цю ситуацію, timer повинен попередити його. Тому в рядках з 9 по 15 відбувається перевірка на досягнення максимального числа повторень сну. Для цього використовується стандартний оператор if, який з часів Сі залишився незмінним, але так само, як і for, втратив дужки. Якщо це останнє повторення, на екран виводиться "Відправляю останні повідомлення ...", а в канал надходить повідомлення "Стоп!", В іншому випадку на екрані з'явиться "Відправляю повідомлення ...", а в канал піде "Продовжуємо". Канали в Go типізовані, тому в канал ch, оголошений з типом chan string, можна відправити тільки рядок (перевірка типів в Go здійснюється під час компіляції, тому помилки легко відловити). У рядку 16 потік підтверджує відправку повідомлення за допомогою друку рядка "Відправив!" на екран.

Як і в Сі, в Go індикатором початку програми є функція main (рядки з 20 по 36), в рамках якої буде виконуватися основний потік. Все, що має зробити наша функція main, це створити новий канал, передати його функції timer, відправити його в окремий потік і чекати результатів.

Щоб отримувати повідомлення з каналу, знадобиться змінна-приймач. Цю роль буде виконувати змінна str типу string, оголошена на початку функції за допомогою ключового слова var (її значенням автоматично стане nil, що еквівалентно NULL в Сі). Для створення каналу використовується вбудована функція make () (рядок 23), яка просто виділяє пам'ять під зазначений тип даних і ініціалізує його нульовим значенням. Крім каналів за допомогою make можна створювати асоціативні масиви і зрізи, для виділення пам'яті використовується new (). Ми не можемо просто оголосити змінну типу chan string і працювати з нею, тому що буфер, який використовується для зберігання переданих по каналу даних, що не буде виділено. Також зверни увагу на неявне оголошення змінної ch, яке відбувається за допомогою оператора ": \u003d" (типізація при цьому зберігається, змінна буде мати тип присвоюється значення). У рядку 24 timer нарешті вирушає в окремий потік. Причому робиться це за допомогою одного-єдиного ключового слова - go.

Тепер, коли timer був відправлений виконувати своє завдання, головному потоку залишається тільки чекати повідомлень. Для прийому повідомлень з потоку в Go використовується вже описаний раніше оператор "<-", который теперь следует направить "из потока в принимающую переменную":

Але якби ми додали в код тільки одну цю рядок, то головний потік продовжив би працювати після отримання першого повідомлення і врешті-решт завершився, чи не обробивши інші повідомлення. Тому нам потрібен нескінченний цикл. Він займає рядки з 26 по 35. Go не має в своєму складі "справжнього" while, тому, якщо потрібно створити умовний оператор циклу, то слід просто помістити умова після ключового слова for і не паритися (або взагалі нічого не вказувати, як це зробив я).

При кожній ітерації циклу в змінну str буде записуватися повідомлення, яке прийшло від потоку timer, і, в залежності від вмісту повідомлення, буде вибиратися той чи інший варіант подальших дій. Зверни увагу, мова дозволяє спокійно порівнювати рядки без всяких підсобних функцій. Крім того, ти можеш отримувати їх зрізи і копії (на манер python або ruby) і обчислювати довжину за допомогою ключового слова len (все це справедливо і по відношенню до масивів).

Для запуску програми потрібно компілятор, який можна завантажити з офіційного сайту мови (правда поки доступні тільки версії для UNIX, Plan9 і MacOS X). Якщо ставити його не хочеться (або у тебе Windows), програму можна запустити, використовуючи спеціальну форму на сайті golang.org (правда, через обмеження на тривалість роботи програми тривалість сну потоку timer доведеться сильно скоротити). Це все.

Стривайте, але ж це не многопоточность?

Так, ти напевно помітив, що з-за блокування каналів навіть на багатоядерному процесорі одночасно активним буде тільки один потік нашої програми, тоді як інший чекатиме відправку / прийом повідомлення. Це дійсно так, і для вирішення цієї проблеми Go має ряд засобів.

  1. Канали можна перевіряти на наявність повідомлень. Якщо рядок "str \u003d<-ch " заменить на " str, ok = <-ch ", то головной поток не будет заблокирован, даже если в канале нет сообщения. Вместо этого в переменную ok будет записано значение false и работа потока продолжится. Естественно, дальше можно поместить проверку на " ok == false " и успеть выполнить какую-то полезную работу, а затем начать новую итерацию цикла и вновь попробовать получить значение. Кстати, таким же образом можно выполнить проверку в потокеотправителе:

ok: \u003d ch<- "Продолжаем"

  1. Канали в Go можуть бути Буферізірованний, тобто вміти накопичувати певну кількість повідомлень до того, як відсилає сторона буде заблокована. Для цього достатньо всього лише додати один додатковий аргумент в виклик функції make:

ch: \u003d make (chan string, 10) // створити канал з буфером в 10 позицій

Зауважу, проте, що в нашій програмі це не дасть результату. Під час сну потік timer не зможе заповнити канал повідомленнями одномоментно, так як після кожного його засипання управління все одно буде переходити головному потоку.

  1. У програму можна додати одну або кілька функцій і відправити їх в окремі потоки, тоді вони будуть спокійно працювати абсолютно паралельно, а коли таймер "продзвенить" задану кількість разів, все вони будуть знищені разом з головним потоком. Однак, якщо ми захочемо отримати результат від цих потоків через канал, то знову зайдемо в блокування або будемо змушені робити безліч перевірок на наявність повідомлень в каналах, як описано в першому пункті. Але цього можна уникнути, якщо застосувати "оператор вибору потоків":

select (
case str \u003d<-ch1:
// обробляємо повідомлення від першого потоку
case str \u003d<-ch2:
// обробляємо повідомлення від другого потоку
case str \u003d<-ch3:
// обробляємо повідомлення від третього потоку
}

Програма буде заблокована на операторі select до того моменту, поки в одному з каналів не з'явиться повідомлення. Після цього буде виконаний відповідний блок. Щоб після обробки одного повідомлення select знову переходив до прослуховування каналів, його слід помістити всередину нескінченного циклу. При цьому, якщо в моменти між надходженнями повідомлень потік повинен виконувати якусь роботу, то її слід помістити в блок default всередині select.

Оператор select дуже широко використовується в Go-програмуванні, саме з його допомогою прийнято створювати "диспетчери повідомлень", які розгалужуються програму на безліч потоків відразу після старту, а потім входять в нескінченний цикл і починають обробку прийшли від них повідомлень. В операційній системі Inferno (всі додатки якій написані на Go-предка Limbo) таким чином реалізований багатовіконний графічний інтерфейс.

висновки

Go поки ще молодий, але дуже перспективний мову, при створенні якого був врахований більш ніж тридцятирічний досвід в області розробки операційних систем і мов програмування (Роб Пайк двадцять років займався дослідженнями в області багатопотокового програмування, протягом яких були створені мови Squeak, NewSqueak і Limbo ). Go продуктивний, доброзичливий до програмістам і красивий.

Освоївши цю мову, ти зможеш писати програми, які будуть набагато ефективніше за все того, що написано на традиційних мовах програмування.

Що на ньому пишуть?

І що можна написати?

Що завгодно. Насправді що завгодно, але, в силу своїх плюсів він дуже популярний для розробки сервер-сайда (бекенд).

Чи варто його вчити?

Питання риторичне. З точки зору купівельному, готовий попит на нього майже відсутня, в силу молодості і нерозкрученості. Так що сенс в ньому є:

  1. Для себе, свого стартапу
  2. Продавати готовий продукт
  3. Виконати замовлення, якщо замовнику підходять плюси цієї мови (доведеться пояснювати / переконувати)

Або він скоро зникне?

Як вже писали - зникнути він не може, тому що OpenSource. Тобто ніхто не відбере у Вас написане на ньому, максимум буде погіршуватися розвиток / підтримка, у що слабо віриться, т.к у мови дуже істотні плюси.

Плюси або «чому я вибрав Go»

продуктивність

За продуктивністю для веб (готові фреймворки) Go програє тільки Java і С / С ++ і нарівні з node.js. При цьому споживання ресурсів істотно нижче, ніж у Java і продуктивність набагато більше, ніж у Python / Ruby.

Нить

При цьому має просто офігенно многопоточную модель в порівнянні з ними. Поки це найкраще, що я зустрічав для многопточкі / асинхронний. При цьому він підтримує і класичні патерни на кшталт м'ютексів і колбеков.

простота

Він дуже простий в освоєнні. Мені здається навіть елементарний, особливо якщо є основа з Python / JavaScript. Є також досить цікава модель успадкування, яка, як мені здається більш прозора ніж класичне ООП, але трохи незвична спочатку.

надійність

Мова цей компільований і статично типізований. Що дає виявлення багатьох помилок задовго до продакшена. Наприклад такі помилки в Python виявляються тільки безпосереднім тестуванням і, якщо тестів немає, то шансів покласти систему дуже багато. Тут же це виключається на етапі компіляції.

швидкість компіляції

І, нарешті, одна з найголовніших фішок - не варто боятися типізації та компіляції. У 145% випадків Вам не доведеться оголошувати тип зміною в коді - він задається автоматично при присвоєнні їй значення. Повідомляти змінні заздалегідь також не потрібно.

Ну і компіляція - головний коник. Час компіляції - це те, на що робиться головний акцент при розробці мови. Воно не відрізняється від часу запуску інтерпретується мови. Тобто система, написана на go з нескомпілірованних початкових кодів запускається приблизно з такою ж швидкістю як система аналогічної складності, написана на интерпретируемом мовою.

Разом

Тобто ми маємо плюси з двох світів - швидкість компіляції / запуску інтерпретується і надійність компилируемого мов. Плюc зверху продуктивність, мультіпарадігменность (можна писати в функціональному стилі), простота і низька ресурсопотребленіе.

Чи підходить це Вам - вирішувати не мені. Мені - підходить, і я вважаю його дуже хорошим вибором для високонавантажених сервісів (і не тільки).

Стефан Нільссон викладає інформатику в Королівському технологічному інституті Стокгольма і дуже багато пише про мову Go. Пропонуємо вам переклад його статті Why Go? - Key advantages you may have overlooked, де він розповідає про головні плюси мови. Стаття орієнтована на читачів, вже знайомих з основами програмування, в тому числі на Java і / або Python.

Вибір мови програмування непросто. Окремі переваги можуть спочатку зачаровувати, а виявлення недоліків вимагає часу і досвіду.

Go - мій основний мову з 2012 року. До цього, з 1998 року, я використовував Джава, а ще раніше - Сі. Python потрібен мені головним чином у викладацькій роботі.

Займатися програмуванням я почав в далекому 1978 році. Тоді я писав для калькулятора TI-57, з його програмною пам'яттю на 50 кроків і 8 регістрами.

мінімалізм

Go - мінімалістичний мову, І це (здебільшого) дуже добре.

Заділ на майбутнє

У Go немає ряду можливостей, звичайних для інших сучасних мов.

Інший приклад: Go НЕ скомпілює програму, якщо вона вимагає якихось пакетів (через import), але в реальності не використовує їх в коді. Такий підхід підвищує ясність коду, а в довгостроковій перспективі - його продуктивність.

Підозрюю, що творці Go навмисне ускладнилидеякі речі. Наприклад, треба дуже постаратися, щоб зловити виняток (паніку). Причому, щоб переступити через тіпобезопасность, вам доведеться позначити свій код ключовим словом unsafe.

Порівняння з Python

В Python фрагмент коду del a [i] видаляє елементи з індексом i зі списку a. Цей код, звичайно, цілком читаємо, але не прозорий: Легко випустити з виду, що тимчасова складність алгоритму представлена \u200b\u200bяк O (n), де n - число елементів списку.

У Go немає такої корисної функції. це не так зручно, зате прозоріше. Якщо ви копіюєте елемент списку, вам потрібно явно вказати це в коді. Дивіться приклад коду: 2 способи видалити елемент з зрізу в Go. Але можна і простіше - за допомогою аppend.

Порівняння з Java

Прозорість коду - проблема не надумана. Ось пара прикладів того, як правила ініціалізації пакетів і порядку виконання коду в Go спрощують підтримку і доопрацювання проекту:

  • Циклічні залежності можуть вести до небажаних наслідків. На відміну від коду на Java, Go-програма з зацикленої инициализацией НЕ скомпілюється.
  • Програма на Go завершує роботу тільки з функції main. Java-додаток закривається після завершення всіх призначених для користувача потоків, які не є демонами.

Це означає, що для розуміння особливостей в роботі Java-додатка потрібно вивчити великі фрагменти його коду. Це може бути і зовсім неможливо, якщо ви використовуєте сторонні бібліотеки.

сумісність

Мова, який схильний до раптових змінабо втрачає підтримку, Може погубити ваш проект.

Для першої версії Go були коротко і стисло сформульовані гарантії сумісності для мовного «ядра» і стандартних бібліотек: програми на Go, які працюють сьогодні, повинні працювати і з майбутніми версіями Go 1. До сих пір зворотна сумісність дотримується бездоганно.

Go - це проект з відкритим кодом і BSD-подібною ліцензією, яка дозволяє комерційне використання, внесення змін, поширення та особисте користування.

Права інтелектуальної власності належать

На жаль, на горизонті згущуються хмари. Причиною тому - судовий розгляд між Oracle America і Google про сутність комп'ютерного коду, авторське право і нової моделі ліцензування Java від Oracle.

продуктивність

Зовні Go непомітний, але під капотом у нього - прекрасно налагоджений движок.

Безглуздо обговорювати продуктивність поза контекстом. Такі параметри програми, як час виконання і споживання пам'яті, сильно залежать від алгоритмів, структур даних, вхідних даних, майстерності програміста, операційної системи і «заліза».

Проте помітно впливати на продуктивність можуть мова, середовище виконанняі стандартні бібліотеки. Все це стосується високорівневих задач і архітектурних рішень. Щоб глибше вникнути в реалізацію компілятора і його продуктивність, читайте FAQ по Go.

Перш за все, Go - компільованиймова. Готове до запуску Go-додаток зазвичай виглядає як один виконуваний файл без окремих динамічних бібліотек або віртуальних машин, які можна було б безпосередньо розгортати.

Обсяг і швидкість генерації машинного коду залежать від цільової архітектури. Генерація коду Go досить і підтримує всі основні ОС (Linux, macOS, Windows) та архітектури (Intel x86 / x86-64, ARM64, WebAssembly, ARM і ін.). Тому від go-додатків можна чекати продуктивності на рівні C ++ або Java. Виграш щодо інтерпретується коду на Python може бути величезним.

У Go є збирач сміття, Що запобігає витоку пам'яті. Причому затримка в роботі збирача мінімальна. На ділі ви можете навіть не помічати, що «потік-сміттяр» запущений.

Практично все стандартні бібліотекивиконані дуже якісно: їх код оптимізований по ефективним алгоритмам. Наприклад, регулярні вирази в Go працюють настільки добре, що час виконання безпосередньо залежить від обсягу введення. На жаль, з Java і Python все інакше.

швидкість збіркив абсолютних величинах зараз досить хороша. Що ще важливіше, Go спроектованийтак, щоб спрощувати компіляцію і аналіз залежностей. Це дозволяє створювати легко масштабованірішення для зростаючих проектів.

Golang, або Go - мова програмування, початок якого було покладено в 2007 році співробітниками компанії Google: Робертом Грізмер, Робом Пайком і Кеном Томпсоном.

За допомогою механізмів многопоточности Go спрощує розподіл обчислень і мережевої взаємодії, а сучасні типи даних відкривають програмісту світ гнучкого і модульного коду. Програма швидко компілюється, при цьому є збирач сміття і підтримується рефлексія.

Це швидкий, статично типізований, компільований мову, при використанні якого створюється враження використання динамічно типизированного і інтерпретується мови.

Розмір програм на Go починається з 3 рядків і може досягати декількох мільйонів, записаних в один або кілька файлів з расшіреніем.go. Сучасні текстові редактори, наприклад, підтримують його синтаксис.

Налаштування оточення

Для початку скачайте 64-бітну версію Go c офіційного сайту. Залежно від операційної системи виконуємо наступні дії.

UNIX / Linux / MacOS X / FreeBSD

Витягуємо скачаний архів в папку / usr / local / go. наприклад:

Tar -C / usr / local -xzf go1.8.3.linux-amd64.tar.gz

Додаємо папку / usr / local / go / bin в змінну оточення PATH:

Export PATH \u003d $ PATH: / usr / local / go / bin

Windows

Використовуємо MSI файл і слідуємо інструкції. За замовчуванням інструменти Go розміщуються в папці С: \\ Go. При установці папка C: \\ Go \\ bin автоматично додасться в системну змінну PATH.

Щоб зміни вступили в силу, перезапустити всі відкриті вікна емуляторів терміналу.

Перевіряємо коректність установки, створивши і виконавши файл C: \\ Projects \\ Go \\ test.go:

Package main import "fmt" func main () (fmt.Println ( "Привіт, Tproger!"))

синтаксис

пакети

Кожна програма на мові Go складається з пакетів (packages). Пакет main - головний, з нього починається виконання програми. У наведеному вище прикладі імпортується пакет fmt.

імпорт

Імпорт пакетів можна описати двома способами.

Import "fmt" import "math"

Import ( "fmt" "math")

функції

Загальна форма визначення функції виглядає наступним чином:

Func function_name ([список параметрів]) [повертаються типи даних] (тіло функції)

Кількість і тип вхідних аргументів може бути будь-яким. Для прикладу опишемо функцію add з двома вхідними параметрами формату int:

Package main import "fmt" func add (a int, b int) int (return a + b) func main () (fmt.Println ( "Сума дорівнює", add (10, 19)))

Виконаємо цей код і отримаємо наступний результат:

змінні

Визначення змінної в Go означає передачу компілятору інформації про тип даних, а так само про місце і обсязі сховища, яке створюється для цієї змінної. Визначати змінні одного типу можна по одному і списком. Загальна форма така:

Var [перелік змінних] [тип даних];

За допомогою оператора var визначаємо перелік змінних, причому тип даних вказуємо в кінці виразу.

Оголошення можливо як на рівні пакета, так і на рівні функції. Розглянемо приклад:

Package main import "fmt" var node, golang, angular bool func main () (var x int fmt.Println (x, node, golang, angular))

Виконаємо цей код і отримаємо наступний результат:

оператор циклу

У мові Go один оператор циклу - це for, який повторює список інструкцій задану кількість разів. Цикл складається з трьох виразів:

  1. Ініціалізація. Виконується перед першою итерацией.
  2. Умова. Обчислюється перед кожною итерацией.
  3. Інкремент. Виконується після кожної ітерації.

Загальна форма циклу виглядає так:

For [умова | (Ініціалізація; умова; інкремент) | діапазон] ([тіло циклу])

При ініціалізації коротко оголошуються змінні, які доступні тільки в межах циклу.

Цикл зупиняє свою роботу, коли умова приймає значення false.

Примітка На відміну від таких мов, як C, Java і JavaScript, перераховані вирази циклу не виділяються дужками. В даному випадку є обов'язковими лише фігурні дужки, що обрамляють тіло циклу.

Розглянемо приклад програми:

Package main import "fmt" func main () (sum: \u003d 0 for i: \u003d 0; i< 8; i++ { sum += i } fmt.Println("Сумма равна ", sum) }

Виконаємо цей код і отримаємо наступний результат:

умовний оператор

Форма визначення умовного оператора в Go виглядає наступним чином:

If [умова] (...)

Якщо вираз в поле умови істинно, код, розміщений між фігурними дужками, виконається.

Умова описується значенням, змінною або виразом. наприклад:

  • true - виконується завжди;
  • a< 10 - выполняется, когда a меньше 10 ;
  • (a< b) || (a < c) - выполняется, когда a меньше b или a меньше c ;
  • (a< b) && (a < c) - выполняется, когда a меньше b и a меньше c .

Розглянемо приклад програми:

Package main import ( "fmt") func main () (if true (fmt.Println ( "Цей вислів виконається завжди")) if false (fmt.Println ( "Цей вислів не виконається ніколи")))

Виконаємо цей код і отримаємо наступний результат:

масиви

Go також підтримує масиви, які являють собою структуру даних фіксованого розміру, що складається з елементів одного типу. Масиви використовуються для зберігання наборів даних, але корисно мати на увазі під ними колекцію змінних одного типу.

Щоб оголосити масив, необхідно вказати тип і необхідну кількість елементів в такий спосіб:

Var наіменованіе_переменной [розмір] тіп_переменной

Наприклад, щоб створити масив balance, що складається з десяти елементів типу float32, використовуємо такий вираз:

Var balance float32

Якщо значення елементів відомі, для визначення масиву допустимо використовувати наступну конструкцію:

Var balance \u003d float32 (1000.0, 2.0, 3.4, 7.0, 50.0)

Довжина масиву не змінюється, тому що є частиною типу даних. Це здається обмеженим рішенням, але не хвилюйтеся: в Go реалізована комфортна робота з масивами.

Розглянемо приклад програми:

Package main import "fmt" func main () (var a string a \u003d "Привіт" a \u003d "Tproger" fmt.Println (a, a) fmt.Println (a) primes: \u003d int (2, 3, 5, 7 , 11, 13) fmt.Println (primes))

Виконаємо цей код і отримаємо наступний результат:

зрізи

Зрізи (Slices) в Go - абстракція над масивами. Хоча вбудованих способів збільшити розмір масиву динамічно або зробити вкладений масив в Go немає, зрізи прибирають це обмеження. Вони надають корисні функції і широко використовуються програмістами.

Оголосити зріз можна як масив, але без вказівки розміру або за допомогою функції make:

Var numbers int / * зріз невизначеного розміру * / / * numbers \u003d int (0,0,0,0,0) * / numbers \u003d make (int, 5,5) / * зріз довжиною і ємністю дорівнює 5 * /

Розмір масиву фіксований, а у зрізу змінюваний. По суті, зріз - більш загальний вигляд масиву.

Тип T - зріз з елементами типу T.

a - зріз 5 елементів масиву a.

Розглянемо приклад програми:

Package main import "fmt" func main () (primes: \u003d int (2, 3, 5, 7, 11, 13) fmt.Println (primes) var s int \u003d primes fmt.Println (s))

Виконаємо цей код і отримаємо наступний результат:

структури

Структура (structure) - призначений для користувача тип даних, який крім іншого комбінує елементи різних типів. Щоб оголосити структуру, використовуємо висловлювання type і struct.

Struct визначає тип даних, якому відповідав би два і більше елементів.

Type пов'язує задане ім'я з описом структури.

Форма опису виглядає наступним чином:

Type struct_variable_type struct (member definition; member definition; ... member definition;)

Як тільки структура типу визначена, він може використовуватися при оголошенні нових змінних:

Variable_name: \u003d structure_variable_type (значення1, значення2 ... значеніеN)

Щоб отримати доступ до елементів структури, використовуємо оператор доступу до елементу. Розглянемо на прикладі:

Package main import "fmt" type Vertex struct (X int Y int) func main () (v: \u003d Vertex (1, 2) v.X \u003d 4 fmt.Println (v.X))

Виконаємо цей код і отримаємо наступний результат:

Чому саме Golang?

Концепція мови Golang розроблена на основі досвіду вирішення повсякденних завдань і не має на меті зробити прорив в програмуванні. Крім того, Go не реалізує ряд функцій, які роблять інші мови (C ++, Java і Python) настільки потужними. Але є три причини, щоб задуматися про використання цієї мови.

читабельність

Як тільки звик до синтаксису Go, прочитати чужий код - тривіальна задача.

У кожної людини власний «правильний» спосіб робити речі, тому Go нав'язує свій стиль програмування. В результаті питання на кшталт використання або невикористання фігурних дужок відпадають, і залишається тільки писати код за заданими правилами.

швидкість

За короткий проміжок часу можна написати швидку програму.

Продуктивність роботи пакета на Go, можливо, буде менше, ніж при використанні С, зате швидкість компіляції для великих програм буде вище. Для більшості проектів - прийнятний компроміс, причому при необхідності досить просто переписати код на мові Go.

менше помилок

Більшість помилок виникають в неперевіреному або складному коді.

Go надає зручні засоби для тестування. Крім того, сувора типізація усуває помилки на кшталт випадкового порівняння кількості яблук з кількістю груш, які визначаються ще на етапі компіляції.

висновок

Якщо хочете поринути глибше в вивчення, подивіться нашу, а головне - почніть створювати маленькі програми, щоб на практиці зрозуміти тонкощі мови. Успіхів!