null

Go вместе изучать Go. Часть 1

Введение

Go (или Golang) — это современный язык программирования, разработанный в Google. Он сочетает в себе простоту и строгость синтаксиса, а также высокую производительность, сопоставимую с C и C++. Go часто используют для разработки высоконагруженных серверных приложений, сетевых сервисов, микросервисов и утилит.

Эта статья познакомит вас с основами языка Go: от структуры программы до работы с функциями, переменными, типами, константами и преобразованием данных.

Пакеты (Packages)

В Go все начинается с пакетов. Пакет — это совокупность файлов, объединённых по функциональности и логике.

Каждая программа на Go обязательно должна содержать пакет main, если это исполняемое приложение.

Входная точка программы — функция main, которая должна находиться в пакете main.

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("Моё любимое число —", rand.Intn(10))
}

В этом примере:

  • package main говорит компилятору, что это основной исполняемый пакет.
  • import (...) подключает внешние пакеты:
  • fmt — для форматированного вывода (аналог printf).
  • math/rand — для генерации случайных чисел.
  • rand.Intn(10) генерирует случайное число от 0 до 9.

Правила:

  • Имя пакета обычно совпадает с последним элементом пути импорта.
  • Если вы пишете библиотеку, вы не используете package main, а называете пакет логично: package utils, package auth и т. д.

Импорты (Imports)

Импорты позволяют подключать пакеты (встроенные или сторонние) в вашу программу.

Существует 2 стиля импорта:

Группированный (факторизованный)

import (
	"fmt"
	"math"
)

По одному

import "fmt"
import "math"

Группированный стиль считается хорошим тоном и улучшает читаемость.

Как это работает?

При импорте Go ищет пакет в стандартной библиотеке, затем — в GOPATH или go.mod (если используется модулирование).

Если пакет не используется, программа не скомпилируется — Go требует чистоту кода.

Пример:

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("Теперь у тебя %g проблем.\n", math.Sqrt(7))
}

Экспортируемые имена (Exported Names)

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

Пример:

import "math"

math.Pi // OK
math.pi // Ошибка — не экспортируется

    Pi — экспортированная переменная (const).

    pi — нет, так как она с маленькой буквы.

Go таким образом реализует инкапсуляцию: всё, что начинается с маленькой буквы — скрыто от внешнего мира.

Почему так?

Это упрощает доступ к публичному API и делает код чище: всё приватное — по умолчанию скрыто.

Функции (Functions)

Функции в Go объявляются с помощью ключевого слова func.

func add(x int, y int) int {
	return x + y
}

  • Имя: add
  • Параметры: x и y типа int
  • Возвращает: int

Если параметры одного типа идут подряд, тип указывается один раз:

func add(x, y int) int {...}

Вызов:

fmt.Println(add(42, 13)) // → 55

Множественные результаты (Multiple Results)

Go позволяет возвращать несколько значений из функции:

func swap(x, y string) (string, string) {
	return y, x
}

a, b := swap("hello", "world") // a = "world", b = "hello"

Это удобно для:

  • Возврата значений и ошибок: (value, error)
  • Обмена значениями
  • Быстрой обработки нескольких результатов

Именованные возвращаемые значения (Named Returns)

Вы можете задать имена возвращаемым значениям прямо в объявлении функции:

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return // без аргументов — вернутся x и y
}

Здесь:

split - имя функции.

sum int - параметр, функция принимает одно целое число.

(x, y int) - именованные возвращаемые значения: функция вернёт два значения, оба типа int, и они уже имеют имена x и y.

Это значит, что внутри функции вы можете использовать x и y как обычные переменные, они уже "задекларированы".

Так как мы задали имена переменным в заголовке функции, компилятор знает, что именно их и нужно вернуть, и мы просто пишем return без всего. Это называется голый return (naked return).

Голый return рекомендуется использовать только в коротких функциях — в длинных это ухудшает читаемость. Например, в длинных функциях, через 20–30 строк кода может быть уже непонятно, какие значения возвращаются, и где они были заданы.

Вот как выглядит та же функция, но без именованных возвращаемых значений:

func split(sum int) (int, int) {
    x := sum * 4 / 9
    y := sum - x
    return x, y // теперь нужно указать явно
}

Переменные (Variables)

Переменные объявляются с помощью ключевого слова var.

var i int
var name string

Можно объявить несколько переменных за раз:

var x, y int
var a, b = 1, "text"

Пример:

var c, python, java bool

func main() {
	var i int
	fmt.Println(i, c, python, java)
}

Инициализация переменных:

Переменные можно инициализировать при объявлении:

var i, j int = 1, 2

Go определеяет тип по значению, поэтому тип можно опустить:

var c, python, java = true, false, "нет!"

Краткое объявление переменных:

Внутри функций можно использовать :=, чтобы сразу объявить и инициализировать переменную:

name := "Go"
age := 15

Это эквивалентно var name string = "Go", но проще.

Важно:

:= можно использовать только внутри функций.

Базовые типы

Go поддерживает множество встроенных типов:

  • Логический: bool
  • Строковый: string
  • Целые, Знаковые: int, int8, int16, int32, int64
  • Целые, Беззнаковые: uint, uint8, uint16, uint32, uint64, uintptr
  • Символы: byte (alias для uint8), rune (alias для int32)
  • С плавающей точкой: float32, float64
  • Комплексные: complex64, complex128

Пример:

var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(-5 + 12i)
)

Нулевые значения (Zero Values)

Если переменной не присвоено значение, она получает нулевое значение:

var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s) //0 0 false ""

Преобразование типов

Go требует явного преобразования типов:

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

Или проще:

i := 42
f := float64(i)
u := uint(f)

Пример:

var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z uint = uint(f)
fmt.Println(x, y, z)

Определение типа (Type inference)

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

v := 42           // int
pi := 3.14        // float64
c := 1 + 2i       // complex128

var i int
j := i // j — это int

Константы (Constants)

Константы объявляются с помощью const. Они могут быть:

  • Числовыми
  • Строковыми
  • Булевыми

Константы нельзя инициализировать через :=.

const Pi = 3.14
const Greeting = "Привет"
const Truth = true

Числовые константы

Константы в Go обладают высокой точностью.

Например:

const (
	Big   = 1 << 100
	Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 { return x * 0.1 }

func main() {
	fmt.Println(needInt(Small))      // OK
	fmt.Println(needFloat(Big))      // OK
	// fmt.Println(needInt(Big))     // Ошибка!
}

Разбор:

1 << 100 означает: сдвинуть число 1 влево на 100 битов.

Это то же самое, что записать единицу с 100 нулями после неё в двоичной системе — получается огромное число, значительно больше, чем помещается в обычный int64.

Пример:

1 << 3 = 8 // 1000 (в двоичной)

1 << 4 = 16 // 10000

1 << 100 = ... // Очень большое число (≈ 1.27 × 10^30)

Small = Big >> 99 — сдвигает Big вправо на 99 битов, т. е. делит его на 2^99.

Получается:

Small = (1 << 100) >> 99 = 1 << 1 = 2

Функции:

func needInt(x int) int {

return x*10 + 1

}

Принимает int, возвращает x * 10 + 1

Например: needInt(2) → 2*10 + 1 = 21

func needFloat(x float64) float64 {

return x * 0.1

}

Принимает float64, возвращает x * 0.1

main():

func main() {

fmt.Println(needInt(Small)) // OK

fmt.Println(needFloat(Small)) // OK

fmt.Println(needFloat(Big)) // OK

}

Подробнее:

needInt(Small)

Small = 2, он подходит для int, т.к. маленький.

→ needInt(2) → 21

needFloat(Small)

Small = 2, Go автоматически приводит 2 к float64

→ needFloat(2.0) → 0.2

needFloat(Big)

Big — огромное значение, но Go позволяет его использовать с float64

Так как Big — это нетипизированная константа, она не привязана к типу, пока не окажется в контексте (float64)

→ Go автоматически подбирает нужный тип (если он помещается в float64)

Что будет, если вызвать needInt(Big)?

Если в main() вы попробуете написать:

fmt.Println(needInt(Big)) 

Вы получите ошибку компиляции:

constant 1267650600228229401496703205376 overflows int

Почему?

Big слишком велик, чтобы быть представленным как int — тип int в Go ограничен (обычно 64 бита).

Go не делает автоматическое приведение из "бесконечно большой" константы к типу, если это приведёт к переполнению.

Ключевые выводы

  • Константы в Go - Могут быть нетипизированными, с высокой точностью до использования.
  • Сдвиг битов - Можно легко делать большие или маленькие числа, сдвигая 1.
  • Автоматическое приведение - Константа без типа может подстроиться под int, float64 и др., если она помещается.
  • Ошибки переполнения - Go откажется компилировать код, если вы передаёте огромное значение в тип с меньшей разрядностью (int).

Заключение

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

Вот основные выводы из изученного:

Структура программы

  • Все Go-программы состоят из пакетов.
  • Точка входа — функция main в пакете main.

Импорты

  • Пакеты подключаются через import, лучше использовать факторизованный (группированный) стиль.
  • Неиспользуемые импорты вызывают ошибку компиляции — Go поощряет чистый код.

Экспорт и инкапсуляция

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

Функции

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

Работа с переменными

  • Переменные объявляются через var или := (внутри функций).
  • Go использует определение типов, упрощая объявления.

Типы

  • Встроенные типы включают int, float64, bool, string, complex128 и другие.
  • Нулевые значения переменных определены по умолчанию.

Константы и точность

  • Константы в Go могут быть нетипизированными и иметь произвольно высокую точность.
  • С помощью битовых сдвигов можно удобно оперировать числами.
  • Go строго следит за преобразованием типов, предотвращая переполнения на этапе компиляции.
Next