Skip to content

Основной синтаксис

Основной синтаксис Go очень прост и понятен, давайте начнём с простейшего примера.

go
package main

import "fmt"

func main() {
   fmt.Println("Hello 世界!")
}
  • Ключевое слово package объявляет, к какому пакету принадлежит текущий go файл. Входной файл должен быть объявлен как пакет main, а входной функцией является функция main. При создании пользовательских пакетов и функций следует избегать дублирования этих имён.

  • import — ключевое слово импорта, после которого следует имя импортируемого пакета.

  • func — ключевое слово объявления функции, используется для объявления функции.

  • fmt.Println("Hello 世界!") — оператор, вызывающий функцию Println из пакета fmt для вывода.

Это было краткое введение в синтаксис, теперь давайте более подробно рассмотрим эти концепции.

Пакеты

В Go программы строятся путём связывания пакетов вместе. Базовой единицей импорта в Go является пакет, а не файл .go. Пакет — это фактически папка (англ. package). Внутри пакета все переменные, константы и определённые типы являются общими. Рекомендуется использовать строчные буквы для именования пакетов, и имена должны быть максимально краткими.

Видимость

Ранее упоминалось, что внутри пакета все переменные, константы и определённые типы являются общими, но для внешних пакетов это не так. Иногда вы не хотите, чтобы другие имели доступ к определённому типу, поэтому необходимо контролировать видимость. Вы могли видеть ключевые слова Public, Private в других ООП языках, но в Go их нет. Способ контроля видимости очень прост, правила следующие:

  • Имя начинается с заглавной буквы — это публичный тип/переменная/константа
  • Имя начинается со строчной буквы или подчёркивания — это приватный тип/переменная/константа

Например, в следующем примере константа MyName является публичной, а константа mySalary — приватной.

go
package example

// Публичная
const MyName = "jack"

// Приватная
const mySalary = 20_000

Это правило видимости применяется во всём языке Go.

Импорт

Импорт пакета для использования его типов/методов/функций/переменных. Синтаксис импорта — import плюс имя пакета:

go
package main

import "example"

При импорте нескольких пакетов можно писать так:

go
package main

import "example"
import "example1"

Можно также использовать скобки, этот метод чаще используется на практике:

go
package main

import (
  "example"
  "example1"
)

Если имена пакетов дублируются или слишком сложны, можно дать им псевдонимы:

go
package main

import (
  e "example"
  e1 "example1"
)

Если псевдоним — подчёркивание _, это анонимный импорт. Анонимно импортированный пакет не может быть использован. Это обычно делается для загрузки функции init из пакета, когда типы из пакета не нужны. Распространённый пример — регистрация драйвера базы данных, когда вам не нужно вручную использовать драйвер.

go
package main

import (
  e "example"
  _ "mysql-driver-go"
)

После импорта, для доступа к типам в пакете, используйте имя_пакета.идентификатор. Например, если вы попытаетесь получить доступ к приватному типу, компилятор сообщит, что доступ невозможен.

go
package main

import (
  "example"
   "fmt"
)

func main() {
    fmt.Println(example.MyName)
}

Существует особый способ импорта — импортировать все типы из пакета в текущую область видимости пакета. При таком способе импорта для доступа к типам больше не нужен оператор ., но если есть типы с одинаковыми именами, компиляция не пройдёт.

go
package main

import (
  . "example"
)

WARNING

В Go нельзя использовать циклический импорт, будь то прямой или косвенный. Например, если пакет A импортирует пакет B, а пакет B импортирует пакет A — это прямой циклический импорт. Если пакет A импортирует пакет C, пакет C импортирует пакет B, а пакет B импортирует пакет A — это косвенный циклический импорт. При наличии циклического импорта компиляция не пройдёт.

Внутренние пакеты

В Go принято, что пакет с именем internal является внутренним пакетом. Внешние пакеты не смогут получить доступ к чему-либо внутри внутреннего пакета, иначе компиляция не пройдёт. Рассмотрим пример:

/home/user/go/
    src/
        crash/
            bang/              (go code in package bang)
                b.go
        foo/                   (go code in package foo)
            f.go
            bar/               (go code in package bar)
                x.go
            internal/
                baz/           (go code in package baz)
                    z.go
            quux/              (go code in package main)
                y.go

Из структуры файлов видно, что пакет crash не может получить доступ к типам из пакета baz.

Комментарии

Go поддерживает однострочные и многострочные комментарии. Рекомендуется оставлять пробел между комментарием и содержимым, например:

go
// Это пакет main
package main

// Импортирован пакет fmt
import "fmt"

/*
*
Это функция запуска main
*/
func main() {
  // Это оператор
  fmt.Println("Hello 世界!")
}

Идентификаторы

Идентификатор — это имя, используемое для именования пакетов, функций, переменных и т.д. Правила именования:

  • Может состоять только из букв, цифр и подчёркиваний
  • Может начинаться только с буквы или подчёркивания
  • Строго различает регистр
  • Не может дублировать любой существующий идентификатор, то есть должен быть уникальным в рамках пакета
  • Не может конфликтовать с любыми встроенными ключевыми словами Go

Ниже приведены все встроенные ключевые слова, также можно посетить Справочное руководство - Идентификаторы для получения более подробной информации:

go
break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Операторы

Ниже приведены операторы, поддерживаемые в Go, в порядке приоритета. Также можно посетить Справочное руководство - Операторы для получения более подробной информации.

Приоритет    Оператор
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

Стоит отметить, что в Go не было выбрано использование ~ в качестве оператора побитового отрицания, вместо этого повторно используется символ ^. Когда два числа используют ^, например a^b, это оператор XOR. Когда используется только с одним числом, например ^a, это оператор побитового отрицания. Go также поддерживает составные операторы присваивания:

go
a += 1
a /= 2
a &^= 2

TIP

В Go нет операторов инкремента и декремента, они были понижены до уровня операторов statement, и они могут располагаться только после операнда, поэтому не нужно纠结 над вопросами типа i++ или ++i.

a++ // правильно
++a // ошибка
a-- // правильно

Также стоит отметить, что они больше не возвращают значение, поэтому запись a = b++ неверна.

Литералы

Литералы, согласно терминологии информатики, — это символы, используемые для выражения фиксированного значения в исходном коде. Также называются литеральными значениями. Оба названия означают одно и то же: что написано, таково и значение — значение в "буквальном смысле".

Целочисленные литералы

Для удобства чтения разрешается использовать подчёркивание _ для разделения цифр, но только после префикса и между цифрами.

go
24 // 24
024 // 24
2_4 // 24
0_2_4 // 24
10_000 // 10k
100_000 // 100k
0O24 // 20
0b00 // 0
0x00 // 0
0x0_0 // 0

Литералы с плавающей точкой

С помощью различных префиксов можно выражать числа с плавающей точкой в разных системах счисления:

go
0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (integer subtraction)

Комплексные литералы

go
0i
0123i         // == 123i
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

Символьные литералы

Символьные литералы должны быть заключены в одинарные кавычки ''. Символы в Go полностью совместимы с UTF-8.

go
'a'
'ä'
''
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'

Escape-последовательности

Доступные escape-последовательности в Go:

\a   U+0007 символ звонка
\b   U+0008 символ возврата
\f   U+000C символ перевода страницы
\n   U+000A символ новой строки
\r   U+000D символ возврата каретки
\t   U+0009 символ горизонтальной табуляции
\v   U+000B символ вертикальной табуляции
\\   U+005C экранирование обратного слеша
\'   U+0027 экранирование одинарной кавычки (действительно только внутри символов)
\"   U+0022 экранирование двойной кавычки (действительно только внутри строк)

Строковые литералы

Строковые литералы должны быть заключены в двойные кавычки "" или обратные кавычки `` (в строках с обратными кавычками экранирование не работает):

go
`abc`                // "abc"
`\n
\n`                  // "\\n\n\\n"
"\n"
"\""                 // `"`
"Hello, world!\n"
"今天天气不错"
"日本語"
"\u65e5\U00008a9e"
"\xff\u00FF"

Функции

В Go объявление функций осуществляется с помощью ключевого слова func, как и в большинстве языков:

go
func main() {
  println(1)
}

Однако в Go функции имеют два отличия. Первое — тип параметра указывается после имени параметра:

go
func Hello(name string) {
  fmt.Println(name)
}

Второе отличие — возможность множественного возвращаемого значения, причём с именами:

go
func Pos() () (x, y float64) {
    ...
}

Стиль кода

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

Ниже приведены некоторые правила, на которые стоит обращать внимание при написании кода.

Перенос фигурных скобок функции

Относительно того, нужно ли переносить открывающую фигурную скобку после объявления функции, почти каждый программист имеет своё мнение. В Go все фигурные скобки не должны переноситься:

go
// Правильный пример
func main() {
  fmt.Println("Hello 世界!")
}

Если вы действительно сделаете так:

go
// Неправильный пример
func main()
{
  fmt.Println("Hello 世界!")
}

Такой код даже не скомпилируется, поэтому Go принудительно требует от всех программистов не переносить скобку после функции.

Отступы в коде

Go по умолчанию использует Tab (символ табуляции) для отступов, и только в особых случаях использует пробелы.

Интервалы в коде

Большинство интервалов в Go имеют значение. В некотором смысле это отражает то, как компилятор воспринимает ваш код. Например, в математических вычислениях:

2*9 + 1/3*2

Как известно, умножение имеет более высокий приоритет, чем сложение. После форматирования интервалы вокруг * будут более компактными, что означает приоритет выполнения, а интервалы вокруг + будут больше, что означает более позднее выполнение.

Пропуск фигурных скобок

В других языках операторы if и for обычно можно сокращать, например:

c
for (int i=0; i < 10; i++) printf("%d", i)

Но в Go это невозможно. Вы можете написать одну строку, но должны добавить фигурные скобки:

go
for i := 0; i < 10; i++ {fmt.Println(i)}

Тернарный оператор

В Go нет тернарного оператора, поэтому следующий код не скомпилируется:

go
var c = a > b ? a : b

Из этой статьи вы можете получить начальное представление о синтаксисе Go. В последующем содержимом эти темы будут раскрыты более подробно.

Golang by www.golangdev.cn edit