Внимание
Данный конспект предназначен исключительно для собственного пользования. Если вы хотите изучить данную тему, то настоятельно рекомендую перейти к первоисточникам:

Основное

  • Всё неиспользуемое является ошибкой компиляции.
  • Выполнение программы происходит в функции main.
  • Функция init вызывается до main (то есть init из всех пакетов, а потом main).
  • Область видимости - внутри блока и выше.
  • $GOPATH структура
    • bin/
    • pkg/
    • src/

Пакеты

  • Пакеты лежат в соответствующих папках.
  • Файлы могут оканчиваться на _darwin, _linux, _windows.
  • _test тест кейсы.
  • Импорт в рамках файла, в котором импортируется.

Константы

  • Принято в UpperCamelCase.
  • Многострочные в скобках принимают значение предыдущей.
  • Спец указатель iota.
const Val = 1

const (
    Val1 = 2
    Val2 // eq prev Val1
)

const (
    Val3 = iota // == 0
    Val4 // iota++ == 1
    _ // пропуск, но iota++
    Val5 // iota++ == 3
)

const (
    Val6 = iota * 3 // == 0
    Val7 // iota++ == 3
    Val8 // iota++ == 6
)

Константы пакета, начинающиеся с маленькой буквы доступны только внутри пакета, с большой извне тоже.

Переменные

  • Тип не может меняться.
  • Объявляются через var с указанием типа после.
  • Можно объявить без var и типа через := но только в функциях.
var val bool = true
var g, m int = 1, 2
var val1, val2 int // не инициализированы

func main() {
    val3 := 5
    fmt.Println(val3)
}

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

Типы

  • nil - отсутствие значения.
  • bool - по-умолчанию false.
    • Стандартные битовые операции.
  • int - по-умолчанию 0.
    • int.
    • int8int64.
    • uint.
    • uint8uint64.
    • Стандартные числовые операции, а также ++, {op}=.
    • Алиасы - byte для int8, rune для int32, int для int32/int64.
    • Поддерживаются битовые операции.
  • float - по-умолчанию 0.0 (есть float32 и float64).
  • complex - существуют complex128 и complex64 (объявляются как в математике 5i + 1).
  • массив - всегда фиксированной длины.
    • ПЕРЕДАЮТСЯ ПО ЗНАЧЕНИЮ.
    • Вместимость и длина совпадают (cap и len).
    • Можно объявить несколькими способами.
var list [5]int
list2 := [...]int {1, 2, 3, 4, 5} // Рассчитает размер автоматически
var list3 = [5]int{1} // 1,0,0,0,0
  • срезы - не имеют фиксированной длины, но имеют вместимость.
    • ПЕРЕДАЮТСЯ ПО ССЫЛКЕ.
    • Можно сделать из массива (по ссылке) как в Питоне arr[:] arr[1:3] и тд.
    • Вместимость и длина (cap и len) могут не совпадать.
    • Суть емкости (вместимости) в том, что если мы выйдем за её пределы, то массив внутри будет пересоздан.
    • Функции.
      • len, cap, make(type, len, cap), copy(to, from), append(slice, val...).
      • Развернуть в аргументы можно с помощью ....
    • Можно объявить несколькими способами.
var slice []int = []int{1} // [1]
slice1 := make([]int, 5, 15) // [0,0,0,0,0]
  • string - "string" и ‵string‵ (как ‘ в PHP), изначально в UTF-8.
    • конкатенация +.
    • Можно брать срезы в UTF-8 или через приведение к []rune.
  • map.
    • ПЕРЕДАЮТСЯ ПО ССЫЛКЕ.
    • Можно создать через make.
var one map[string]int = map[string]int{"a": 1, "w": 2}
two := make(map[int]bool, 16)
three := map[string]string
  • Работа с ключами аналогична массиву, удалить с помощью функции delete.
  • Если обратиться к несуществующему ключу, то вернется значение по-умолчанию.
  • Проверка на существование.
value, exist := someMap["someKey"]
// exist булев тип, которые говорит об установке значения
  • pointer
    • Указатели, это переменные, которые хранят ссылки на другие переменные.
    • Создать ссылку можно с помощью &.
    • Сам указатель имеет тип *{type}, где {type} - тип на который он ссылается.
    • Получить значение можно с помощью *.
one := 1
var ref *int = &one
fmt.Println(one) // 1
fmt.Println(ref) // address of one
fmt.Println(*ref) // 1

Управляющие конструкции

if-else if-else - стандартная конструкция

if false {
    // do something
} else if {
    // do something
} else {

}
  • Можно выполнить предварительно команду if a := 1; a > 1 {}.
  • switch-case.
    • boolcall.
switch {
    case false:
        // do some
    case 2 > 1:
        // do some
    default:
        // do some
}
  • casexpr: есть несколько вариантов (fallthrough - противоположный break).
switch a := "hello"; a {
    case "hel":
        // do some
    case "hello":
        // do some
    default:
        // do some
}

b := "hello";
switch b {
    case "hel":
        fallthrough
    case "hello":
        // do some
    default:
        // do some
}
  • type.
var c interface{}
switch c.(type) {
    case string:
        // do some
    case int:
        // do some
}
  • for - единственный цикл (работает break, continue).
    • без аргументов - бесконечный.
for {
    if a < 1 {
        break
    }
    a--
    continue
    println(1)
}
  • три аргумента (1 обязательный).
for i := 0; i < 10; i++ {
    if i < 1 {
        break
    }
    continue
    println(1)
}
  • обход коллекций.
a := [5]int{1, 2, 3, 4, 5}
for index := range a {
    // a[index]
}

var m = map[string]int{"a": 1, "b": 2}
for k, v := range m {
    println(k,v)
}
  • обход без использования индексов.
for range x {
    fmt.Println("Hello")
}

Функции

  • Объявляются func name(arg type, orArgs type...) returnType {}.
  • Возвращает через return.
  • Есть замыкания.
  • Можно присвоить в переменную.
  • Можно вернуть или передать как аргумент, тип: func (arg type) returnType.
  • Можно вызвать на месте.
func () {
    println("A'm executed")
}()
  • Можно вернуть несколько значений.
var f1 = func() (int, int) {
    return 1, 2
}
var a1, b1 = f1()
println(a1)
println(b1)

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

Области видимости

  • Переменные внутри блока (даже просто в {}) доступны по значению, то есть при переопределении внешняя не меняется.
  • Переменные доступны по ссылке только явно или для срезов массивов, мап.

Инструкция defer

  • Откладывает выполнение функции в конец, как в очередь (то есть выполнения в обратном порядке).

Отлов ошибок

  • try/catch НЕТ.
  • panic(str) - сообщает об ошибке и всплывает.
  • recover() - возвращает ошибку.
  • Конструкция используется совместно с defer.
defer func() {
    if err := recover(); err != nil {
        println(err)
        println("No panic, please")
    }
}()

panic("Something wrong!!!")

Алиасы типов

type I = int64

Теперь можно указывать I как псевдоним для int64.

Пользовательские типы подобны алиасам типов

  • type RichInt int.
  • Таким типам можно добавлять методов так же как и структурам.
type richint int
func (r *richint) toString() string {
    return string(*r)
}
var i4 richint = 1

i4.toString()

Структуры

  • Структуры объявляются как пользовательский тип от struct, внутри блока находятся объявления свойств.
type Person struct {
    name string
}
  • Методы добавляются отдельно с указанием типа.
func (this *Person) sayHi() {
    println("Hi, I am " + this.name)
}
  • Наследование решено за счет встраивания, необходимо добавить безымянное свойство структуре.
type Bob struct {
    Person
    age int
}
  • Ему доступны все свойства родителя, можно передавать в функции как при наследовании.
  • К методам и свойствам можно обратиться 2мя способами.
type Bob struct {
    Person
    age int
}
bb := Bob{age: 45}
bb.name = "Bob"
bb.Person.sayHi()
bb.sayHi()
  • Можно переопределить метод, но иметь к нему доступ через .parent.method().

Интерфейс

Объявляется как

type InterfaceName interface {
    method(args int...) string
}
  • Явно указать наследование нельзя, используется утиная типизация.
  • Есть если у структуры есть метод, то он является подтипом интерфейса.
  • Можно встроить интерфейс в интерфейс.

Горутины

  • С помощью ключевого слова go можно выполнить функцию в отдельной горутине (main тоже выполняется в отдельной).
a := [5]int{1, 2, 3, 4, 5}
var f = func(i int) {
    fmt.Println(i)
}

for i := range a {
    go f(i)
}

С помощью пакета sync.WaitGroup можно ожидать окончания выполнения горутин

Каналы - это специализированный тип для передачи данных между горутинами

  • Создается через make(chan typeOf channel, buffer), где буфер - это количество сообщений.
  • Каналы - ССЫЛКИ.
  • Закрывается через функцию close.
  • Посылаем в канал chanvar <- msg.
  • Получаем из канала <-chanvar.
  • Когда Go доходит до отправки в канал, то он блокирует текущую горутину, ждет пока там будет буфер(место).
  • Поэтому лучше объявить буфер, чтобы не было дедлоков.
package main

import (
    "fmt"
    "time"
    "strconv"
)

func printMsg(name string, ch chan string) {
    for {
        fmt.Println(name + <-ch)
    }
}

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    ch := make(chan string)

    for _, name := range [5]int{1, 2, 3, 4, 5} {
        go printMsg(strconv.Itoa(name) + " : ", ch)
    }

    for _, msg := range [10]string{"A","B","C","D","E","F","J","K","L","M"} {
        ch <- msg
    }
    time.Sleep(1000 * 1000)
    close(ch)
}
  • Можно указать тип на только получение из канала или только запись.
func receive(c <-chan string) {}
func send(c chan <- string) {}
  • select работает подобно switch, но в каждом кейсе указывается канал, откуда получено сообщение.
select {
    case msg := <- ch1:
        // do some
    case msg := <- ch2:
        // do some
    default:
        // do some
}