Рубрики
go

Пример создания проекта golang

0. Создаем директорию для проекта
mkdir ИМЯ_ПРОЕКТА
cd  ИМЯ_ПРОЕКТА
touch main.go

1. Первичная иницилизация
go mod init  ИМЯ_ПРОЕКТА

2. Докача модулей из интернета
go get путь_до_модуля
go get путь_до_модуля
go get путь_до_модуля
go get путь_до_модуля

Сборка:
go build -o ИМЯ_ПРОЕКТА_ЖЕЛАЕМОЕ_ИМЯ_БИНАРНИКА_Х86
GOOS=linux GOARCH=arm64 go build -o ИМЯ_ПРОЕКТА_АРМ64


P.S.
ИМЯ_ПРОЕКТА/
│── go.mod        # Файл с информацией о модулях
│── go.sum        # Контрольные суммы зависимостей
│── main.go       # Исходный код
└── ИМЯ_ПРОЕКТА   # Скомпилированный бинарник (после go build)


Рубрики
go

017 / go slice / сегменты / срезы

Сегменты

Сегменты — разновидность коллекций, 
которые могут расширяться для хранения дополнительных элементов; а это как раз то, что нужно! 
 
Оказывается, в Go существует структура данных, в которую можно добавлять новые значения, 
— она называется сегментом. 
Как и массив, сегмент состоит из нескольких элементов, относящихся к одному типу. 
В отличие от массивов, существуют функции, позволяющие добавлять новые элементы в конец сегмента.
 
Отличие сегмента от массива:
Фактически это уже знакомый синтаксис объявления массива, только без указания размера.
var myArray [5]int // Массив — обратите  внимание на размер.
var mySlice []int  // Сегмент — размер не задан
 
 
В отличие от переменных для массивов, объявление переменной для сегмента не приводит к автоматическому созданию сегмента.
Для этого следует вызвать встроенную функцию make. 
Функции передается тип создаваемого сегмента (он должен соответствовать типу переменной,
которой вы собираетесь присвоить сегмент)
и длина сегмента при создании.
 
Пример:
        var notes []string
        notes = make([]string, 7)
        notes[0] = "do"
        notes[1] = "re"
        notes[2] = "mi"
        fmt.Println(notes[2])
        fmt.Println(notes[0])
 
Пример:
        pr := make([]int, 5)
        pr[0] = 99
        pr[4] = 200
        fmt.Println("pr[4] - pr[0] = ", pr[4]-pr[0])
 
 
Встроенная функция len для сегментов работает так же, как и для массивов.
Передайте len сегмент, и функция вернет его длину в виде целого числа.
Пример:
        notes := make([]string, 7)
        primers := make([]int, 5)
        fmt.Println(len(notes))
        fmt.Println(len(primers))
 
Циклы "for" и "for...range" работают с сегментами точно так же, как и с массивами:
Пример:
        letters := []string{"a", "b", "c"}
        for i := 0; i < len(letters); i++ {
                fmt.Println(letters[i])
        }
        fmt.Println(" ")
        for _, letter := range letters {
                fmt.Println(letter)
        }

Литералы сегментов

Как и с массивами, если вы заранее знаете, какими значениями должен быть заполнен сегмент в исходном
состоянии, то можете инициализировать сегмент этими значениями при помощи литерала сегмента.
Литерал сегмента очень похож на литерал массива, но если литерал массива содержит длину массива в квадратных скобках, 
у литерала сегмента квадратные скобки пусты. 
За пустыми скобками следует тип элементов, которые будут храниться в сегменте, и список исходных значений всех элементов, заключенный в фигурные скобки.
Вызывать функцию make необязательно, при использовании литерала сегмента ваш код создаст сегмент и заполнит его.
 
Сегмент:
 []int{9, 18, 27}
 |  |     |
 |  |     Список значений, разделенных запятыми.
 |  Тип элементов в сегменте
 Пустая пара квадратных скобок
 
 
Пример:
notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"} //Значения присваиваются с помощью литерала сегмента.
fmt.Println(notes[3], notes[6], notes[0])
primes := []int{ //  Многострочный литерал сегмента.
2,
3,
5,
}
fmt.Println(primes[0], primes[1], primes[2])
 
Погодите! 
Похоже, что сегменты могут делать все, что делают массивы, и в них можно добавлять элементы. 
Тогда почему бы не ограничиться сегментами и не забыть про эту ерунду с массивами?
!!! Потому что сегменты построены на основе массивов.
!!! И вы не сможете понять, как работают сегменты, не понимая массивы. 
 
Каждый массив существует на основе базового массива. 
Данные сегмента на самом деле хранятся в базовом массиве, 
а сегмент всего лишь предоставляет «окно» для работы с некоторыми (или всеми) элементами массива.
Когда вы используете функцию make или литерал сегмента для создания сегмента, 
базовый массив при этом создается автоматически (и вы не можете обратиться к нему иначе как через сегмент). 
Но вы также можете создать массив самостоятельно, а затем создать сегмент на основе этого массива при помощи оператора сегмента.
 
Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice1 := underlyingArray[0:3]
        fmt.Println(slice1)
 
Пример: 
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        i, j := 1, 4
        slice2 := underlyingArray[i:j]
        fmt.Println(slice2)
 
У оператора сегмента предусмотрены значения по умолчанию как для начального, так и для конечного индексов. 
Если начальный индекс не указан, будет использовано значение 0  позиции.
Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice4 := underlyingArray[:3]
        fmt.Println(slice4)
 
А если не указан конечный индекс, то в сегмент включаются элементы от начального индекса и до конца базового завершается.
Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice5 := underlyingArray[1:]
        fmt.Println(slice5)

Базовые массивы

Как упоминалось ранее, сам сегмент не содержит данных, 
это всего лишь «окно» для просмотра элементов базового массива.
Сегмент можно представить себе как микроскоп, 
направленный на определенную часть предметного стекла (базовый массив).
 
Когда вы берете сегмент базового массива, то «видите» только ту часть элементов массива,
которая видна через этот сегмент.
 
Несколько сегментов могут существовать на основе одного базового массива.
В этом случае каждый сегмент становится «окном» для отдельного подмножества элементов массива.
 Сегменты даже могут перекрываться!
 
Присваивание нового значения элементу сегмента приводит 
к изменению соответствующего элемента в базовом массиве.
 
Если на один и тот же базовый массив указывают несколько сегментов, 
то и изменения элементов массива будут видны во всех сегментах.
 
Из-за этих потенциальных проблем обычно рекомендуется создавать сегменты с использованием make или литерала
сегмента (вместо того, чтобы создать массив и применять к нему оператор сегмента). 
С make и литералами сегментов вам никогда не приходится иметь дела с базовым массивом.

Расширение сегментов функцией «append»

В Go существует встроенная функция append, которая получает сегмент и одно или несколько значений, 
которые присоединяются в конец сегмента. 
Функция возвращает но вый, расширенный сегмент со всеми элементами исходного сегмента и новыми элементами, 
добавленными в его конец.
 
Пример:
        slice := []string{"a", "b"}
        fmt.Println(slice, len(slice))
        slice = append(slice, "c")
        fmt.Println(slice, len(slice))
        slice = append(slice, "d", "e")
        fmt.Println(slice, len(slice))
 
 
Вам не нужно следить за тем, по какому индексу присваиваются новые значения, или за чем-нибудь еще! 
Просто вызовите функцию append и передайте ей сегмент со значениями, которые добавляются в конец сегмента, 
и вы получите новый расширенный сегмент. 
Да, так просто!
 
 
Обратите внимание: возвращаемое значение append во всех случаях присваивается той же переменной сегмента, 
которая передается append. 
Таким образом предотвращается возможность непоследовательного поведения сегментов, возвращаемых append.
 
Базовый массив сегмента не может увеличиваться в размерах. 
Если в массиве не остается места для добавления элементов, все элементы копируются в новый, больший массив, 
а сегмент обновляется, чтобы он базировался на новом массиве. 
Но поскольку все это происходит где-то за кулисами внутри функции append, 
невозможно простым способом определить, имеет ли возвращенный сегмент тот же базовый массив,
 как и переданный сегмент, или другой. 
Если в программе будут оставаться оба сегмента, это может привести к непредсказуемому поведению.
 
Пример:
       s1 := []string{"s1", "s1"}
       s2 := append(s1, "s2", "s2")
       s3 := append(s2, "s3", "s3")
       s4 := append(s3, "s4", "s4")
       fmt.Println(s1, s2, s3, s4)
       s4[0] = "XX"
       fmt.Println(s1, s2, s3, s4)
 
По этой причине при вызове append возвращаемое значение обычно присваивается той же переменной сегмента, 
которая была передана append. 
Если в программе хранится только один сегмент, то вам не придется беспокоиться о том,
используют ли два сегмента один базовый массив!
Пример:
        s1 := []string{"s1", "s1"}
        s1 = append(s1, "s2", "s2")
        s1 = append(s1, "s3", "s3")
        s1 = append(s1, "s4", "s4")
        fmt.Println(s1)

Сегменты и нулевые значения

Как и в случае с массивами, при обращении к элементу сегмента, 
которому не было присвоено значение, вы получите нулевое значение для этого типа.
Пример:
        floatSlice := make([]float64, 10)
        boolSlice := make([]bool, 10)
        fmt.Println(floatSlice[9], boolSlice[5])
 
Пример:
        var intSlice []int
        var stringSlice []string
        fmt.Printf("intSlice: %#v, stringSlice: %#vn", intSlice, stringSlice)
Рубрики
go

016 / go работа с файлами

Чтение текстового файла

Ранее мы использовали пакеты os и bufio стандартной библиотеки для чтения данных по строкам с клавиатуры. 
Те же пакеты могут использоваться и для построчного чтения данных из текстовых файлов. 
 
В своем любимом текстовом редакторе создайте новый файл с именем data.txt.
Запишите в файле три наших значения с плавающей точкой, по одному числу в строке.
cat > data.txt << "EOF"
71.8
56.2
89.5
EOF
 
Пример программы:
package main
import (
        "bufio"
        "fmt"
        "log"
        "os"
)
func main() {
 
        file, err := os.Open("data.txt") //Файл данных открыва
        if err != nil {
                log.Fatal(err)
        }
        scanner := bufio.NewScanner(file)
        //Цикл выполняется до того, как будет  достигнут конец  файла, а scanner.Scan вернет false
        for scanner.Scan() { //Читает строку из файла
                fmt.Println(scanner.Text()) //Выводит строку
        }
        // Если при закрытии файла произошла ошибка то сообщить о ней и завершить работу
        err = file.Close() //Закрывает файл для освобождения ресурсов.
        if err != nil {
                log.Fatal(err)
        }
        if scanner.Err() != nil {
                log.Fatal(scanner.Err())
        }
}
 
Наша тестовая программа readfile.go успешно читает данные из файла data.txt и выводит их. 
А теперь разберемся, как же она работает.
Сначала строка с именем открываемого файла передается функции os.Open. 
Эта функция возвращает два значения: указатель os.File, представляющий открытый файл, и значение ошибки. 
Как и в случае с другими функциями, если значение ошибки равно nil, это означает, 
что файл был открыт успешно, но любое другое значение указывает на то, 
что произошла ошибка (например, если файл отсутствует или не читается). 
В таком случае программа выводит сообщение об ошибке и завершается.

Чтение текстового файла в массив

Пример кода:
// Пакет datafile предназначен для чтения данных из файлов.
//package datafile
package main
 
import (
        "fmt"
        "bufio"
        "os"
        "strconv"
)
 
// GetFloats читает значение float64 из каждой строки файла. ошибку.
func GetFloats(fileName string) ([3]float64, error) {
        var numbers [3]float64 // Объявление возвращаемого массива.
 
        file, err := os.Open(fileName) //Открывает файл с переданным именем.
        if err != nil {
                return numbers, err
        }
 
        i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание
 
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
                numbers[i], err = strconv.ParseFloat(scanner.Text(), 64)
 
                if err != nil {
                        return numbers, err
                }
                i++ //Переход к следующему индексу массива.
        }
 
        err = file.Close()
        if err != nil {
                return numbers, err
        }
 
        if scanner.Err() != nil {
                return numbers, scanner.Err()
        }
        return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil          ».
}
 
func main() {
        fileName := "data.txt"
        fmt.Println("Hello readfile")
        fmt.Println(GetFloats(fileName))
}

Чтение файла и выполнение программы average. Еще один пример:

//average вычисляет среднее значение
package main
 
import (
        "bufio"
        "fmt"
        "os"
        "strconv"
        "log"
)
 
// GetFloats читает значение float64 из каждой строки файла. ошибку.
func GetFloats(fileName string) ([3]float64, error) {
        var numbers [3]float64 // Объявление возвращаемого массива.
 
        file, err := os.Open(fileName) //Открывает файл с переданным именем.
        if err != nil {
                return numbers, err
        }
 
        i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание
 
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
                numbers[i], err = strconv.ParseFloat(scanner.Text(), 64)
 
                if err != nil {
                        return numbers, err
                }
                i++ //Переход к следующему индексу массива.
        }
 
        err = file.Close()
        if err != nil {
                return numbers, err
        }
 
        if scanner.Err() != nil {
                return numbers, scanner.Err()
        }
        return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil».
}
 
func main() {
        filename := "data.txt"
        numbers, err := GetFloats(filename)
        if err != nil {
                log.Fatal(err)
        }
        var sum float64 = 0
        for _, number := range numbers {
                sum += number
        }
        sampleCount := float64(len(numbers))
        fmt.Printf("Average: %0.2fn", sum/sampleCount)
}
 
Наша программа может обрабатывать только три значения!
 
cat > data1.txt << "EOF"
71.8
56.2
89.5
99.3
EOF
 
Пример ошибки:
panic: runtime error: index out of range [3] with length 3
goroutine 1 [running]:
main.GetFloats(0xd5676, 0x9, 0x0, 0x0, 0x0, 0x4d458, 0x162060)
        /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:25 +0x2e4
main.main()
        /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:46 +0x34
exit status 2

Рубрики
go

014 / go doc

Введите команду "go doc", чтобы вывести документацию по любому пакету или функции.
Чтобы вывести документацию по пакету, передайте его путь импорта команде "go doc". 
Например, информация о пакете "strconv" выводится командой "go doc strconv".
 
go doc -h
 
go doc -u http
 
go doc strconv
 
go doc strconv ParseFloat
 
go doc github.com/headfirstgo/keyboard
 
Команда go doc старается получить полезную информацию на основании анализа кода. 
Имена пакетов и пути импорта включаются автоматически, как и имена функций, параметры и возвращаемые типы. 
И все же команда go doc не умеет творить чудеса. 
Если вы хотите, чтобы пользователи смогли узнать из документации о предназначении пакета или функции, 
придется добавить эту информацию самостоятельно.
 
К счастью, это делается просто: нужно добавить в код документирующие комментарии. 
Обычные комментарии Go, размещенные непосредственно перед директивой package или объявлением функции, 
считаются документирующими комментариями и включаются в вывод go doc.
 
 
При добавлении документирующих комментариев следует соблюдать ряд правил:
- Комментарии должны состоять из полноценных предложений.
- Комментарии для пакетов должны начинаться со слова "Package", за которым следует имя пакета:
// Package mypackage enables widget management.
- Комментарии для функций должны начинаться с имени функции, которую они описывают:
// MyFunction converts widgets to gizmos.
- В комментарии также можно включать примеры кода, которые должны снабжаться отступами.
- Не включайте дополнительные символы для выразительности или форматирования (кроме отступов в примерах кода). 
  Документирующие комментарии будут выводиться в виде простого текста и должны форматироваться соответствующим образом.


go help
go help gopath
go help list
Рубрики
go

015 / go arrays / Массивы

Массивы

Многие программы работают со списками. 
Списки адресов. 
Списки телефонных номеров. 
Списки товаров.
 
Массив представляет собой набор значений, относящихся к одному типу. 
Представьте себе таблетницу — вы можете класть и доставать таблетки в любое отделение, 
но при этом ее удобно переносить как единое целое.
 
 
Значения, хранящиеся в массиве, называются элементами. 
Вы можете создать массив строк, массив логических значений или массив любого другого типа Go (даже массив массивов). 
Весь массив можно сохранить в одной переменной, а затем обратиться к любому нужному элементу.
 
В массивах хранятся наборы значений.
 
Массив содержит заранее заданное количество элементов, а его размер не может увеличиваться или уменьшаться. 
 
Чтобы объявить переменную для хранения массива, следует указать количество хранящихся в нем элементов в квадратных скобках ([]),
а затем тип элементов в массиве.
 
Чтобы присвоить значения элементам массива или прочитать их позднее, необходимо каким-то образом указать, какой элемент вам нужен. 
Элементы в массиве нумеруются, начиная с 0. 
Номер массива называется его индексом.
 
Пример массив нот:
var notes [7]string 
notes[0] = "do" 
notes[1] = "re" 
notes[2] = "mi" 
fmt.Println(notes[0]) 
fmt.Println(notes[1])
 
Массив целых чисел:
var primes [5]int 
primes[0] = 2 
primes[1] = 3 
fmt.Println(primes[0])
 
Массив значений time.Time:
var dates [3]time.Time 
dates[0] = time.Unix(1257894000, 0)
dates[1] = time.Unix(1447920000, 0) 
dates[2] = time.Unix(1508632200, 0) 
fmt.Println(dates[1])
 
 
Нулевые значения в массивах
 
Как и в случае с переменными, 
при создании массивов все содержащиеся в них значения инициализируются нулевым значением для типа, 
содержащегося в массиве. 
Так массив значений int по умолчанию заполняется нулями.
 
С другой стороны, нулевым значением для строк является пустая строка, 
так что массив строковых значений по умолчанию заполняется пустыми строками.
 
Нулевые значения позволяют безопасно выполнять операции с элементами массивов, даже если им не были присвоены значения. 
Например, в следующем массиве хранятся целочисленные счетчики. 
Любой элемент можно увеличить на 1 даже без предварительного присваивания значения, 
потому что мы знаем, что все значения счетчиков начинаются с 0.
 
var counters [3]int
counters[0]++ // Первый элемент увеличивается с 0 до 1.
counters[0]++ // Первый элемент увеличивается с 1 до 2.
counters[2]++ // Третий элемент увеличивается с 0 до 1.
fmt.Println(counters[0], counters[1], counters[2])
Вывод:
2 0 1
 
При создании массива все содержащиеся
в нем элементы инициализируются нулевым
значением для типа, хранящегося в массиве.

Литералы массивов

Если вам заранее известны значения, которые должны храниться в массиве, 
вы можете инициализировать массив этими значениями в форме литерала массива. 
Литерал массива начинается как тип массива — с количества элементов в квадратных скобках,
за которым следует тип элементов. 
Далее в фигурных скобках идет список исходных значений элементов массива. 
Значения элементов должны разделяться запятыми.
 
[3]int{9, 18, 27}
 |  |      |
 |  |      Список значений, разделенных запятыми.
 |  Тип элементов в массиве.
 |
 Количество элементов в массиве.
 
 
var notes [7]string = [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
fmt.Println(notes[3], notes[6], notes[0])
var primes [5]int = [5]int{2, 3, 5, 7, 11}
fmt.Println(primes[0], primes[2], primes[4])
 
Литералы массивов также позволяют использовать короткие объявления переменных с помощью :=.
Короткое объявление переменной.
notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
primes := [5]int{2, 3, 5, 7, 11}
 
 
Литералы массивов могут распространяться на несколько строк, но перед каждым переносом строки в коде должна стоять запятая. 
Запятая даже должна стоять после последнего элемента в литерале массива, если за ним следует перенос строки. 
(На первый взгляд этот синтаксис выглядит неуклюже, но он упрощает последующее добавление новых элементов в коде.)
 
text := [3]string{ // Все это один массив.
"This is a series of long strings",
"which would be awkward to place",
"together on a single line", // Запятая в конце обязательна.
}
 
 
Когда вы занимаетесь отладкой кода, вам не нужно передавать элементы массивов Println
и другим функциям пакета fmt один за одним. 
Просто передайте весь массив. 
Пакет fmt содержит логику форматирования и вывода массивов. 
(Пакет fmt также умеет работать с сегментами, картами и другими структурами данных, которые будут описаны позднее.)
var notes [3]string = [3]string{"do", "re", "mi"}
var primes [5]int = [5]int{2, 3, 5, 7, 11}
fmt.Println(notes)
fmt.Println(primes)
 
Возможно, вы также помните глагол "%#v", используемый функциями Printf и Sprintf, — он форматирует значения так, как они отображаются в коде Go. 
При форматировании с "%#v" массивы отображаются в форме литералов массивов Go.
fmt.Printf("%#vn", notes)
fmt.Printf("%#vn", primes)

Обращение к элементам массива в цикле

Вы не обязаны явно записывать целочисленные индексы элементов массивов, к которым обращаетесь в своем коде. 
В качестве индекса также можно использовать значение целочисленной переменной.
 
notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
index := 1
fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 1.
index = 3
fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 3.
 
for i := 0; i <= 2; i++ {
fmt.Println(i, notes[i])
}
 
При обращении к элементам массивов через переменную необходимо действовать внимательно и следить за тем, 
какие значения индексов используются в программе. 
Как упоминалось ранее, массивы содержат конкретное число элементов. 
Попытка обратиться к индексу за пределами массива приводит к панике — ошибке, 
происходящей во время выполнения программы (а не на стадии компиляции).

Проверка длины массива функцией «len»

Написание циклов, которые ограничиваются только правильными индексами, сопряжено с определенным риском ошибок. 
К счастью, есть пара приемов, которые упрощают этот процесс.
Во-первых, вы можете проверить фактическое количество элементов в массиве перед обращением к элементу. 
Для этого можно воспользоваться встроенной функцией len, которая возвращает длину массива (количество содержащихся в нем элементов).
 
notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
for i := 0; i < len(notes); i++ {
fmt.Println(i, notes[i])
}
 
Впрочем, и здесь существует некоторый риск ошибок. 
Хотя  len(notes) возвращает наибольший индекс, к которому вы можете обращаться, 
равен 6 (потому что индексирование массивов начинается с 0, а не с 1). 
При попытке обратиться по индексу 7 возникнет ситуация паники. 

Безопасный перебор массивов в цикле «for…range»

В другом, еще более безопасном способе обработки всех элементов массива используется специальный цикл for...range. 
В форме с range указывается переменная для хранения целочисленного индекса каждого элемента, 
другая переменная для хранения значения самого элемента и перебираемый массив. 
Цикл выполняется по одному разу для каждого элемента в массиве; 
индекс элемента присваивается первой переменной, а значение элемента — второй переменной. 
В блок цикла включается код для обработки этих значений.
 
 
for index, value := range myArray {
// Блок цикла.
}
 
Эта форма цикла for не содержит запутанных выражений инициализации, условия и завершения. 
А поскольку значение элемента автоматически присваивается переменной, риск обращения к недействительному индексу массива исключен. 
Форма цикла for с range читается безопаснее и проще, поэтому именно она чаще всего  встречается при работе с массивами и другими коллекциями.
 
notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
for index, note := range notes {
fmt.Println(index, note)
}
Цикл выполняется семь раз, по одному разу для каждого элемента в массиве notes. 
Для каждого элемента переменной index присваивается индекс элемента, 
а переменной note присваивается значение элемента. После этого мы выводим индекс и значение. 
 
Помните, как при вызове функции с несколькими возвращаемыми значениями мы хотели проигнорировать одно из них? 
Это значение присваивалось пустому идентификатору ( _ ), 
чтобы компилятор Go просто отбросил это значение без выдачи сообщения об ошибке...
То же самое можно проделать со значениями из циклов "for...range". 
Если вам не нужен индекс каждого элемента массива, присвойте его пустому идентификатору:
 
        notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
        for index, note := range notes {
                fmt.Println(index, note)
        }
А если вам не нужна переменная для значения, замените ее пустым идентификатором

Рубрики
go

013 / go Указатели

Указатели

Оператор & (амперсанд) используется в Go для получения адреса переменной. 
Например, следующий код инициализирует переменную, сначала выводит ее значение, а затем адрес переменной...
 
Пример кода:
package main
import "fmt"
func main() {
        amount := 6
        fmt.Println(amount)  //Выводит значение переменной
        fmt.Println(&amount) //Выводит адрес переменной
}
 
вывод кода:
6 //Значение переменной.
0x400011c010 //Адрес переменной.
 
 
Адрес можно получить для переменной любого типа. 
Обратите внимание, все переменные имеют разные адреса.
 
Еще пример кода:
package main
import "fmt"
func main() {
        var myInt int
        fmt.Println(&myInt)
 
        var myFloat float64
        fmt.Println(&myFloat)
 
        var myBool bool
        fmt.Println(&myBool)
 
}
 
Вывод кода:
0x40000ba010
0x40000ba018
0x40000ba030
 
 
И что же собой представляют эти «адреса»? 
Чтобы найти конкретный дом в плотно застроенном городе, вам нужно знать его адрес...
Память, выделяемая компьютером программе, так же переполнена, как и городские улицы. 
Она забита значениями переменных: логическими значениями, целыми числами, строками и т. д. 
Зная адрес переменной, вы сможете воспользоваться им для получения значения, хранящегося в переменной.
 
               Адрес 
               |
0x1040a100 0x1040a108 0x1040a110 0x1040a118 0x1040a120 0x1040a128
true          6          ...        ...        ...       3.1415
              |
              значение, хранящееся по этому адресу!
 
!!! Значения, представляющие адреса переменных, называются указателями, потому что они указывают на область памяти, в которой хранится переменная.

Типы указателей

Тип указателя состоит из знака * и типа переменной, на которую ссылается указатель. 
Например, тип указателя на переменную int записывается в виде *int (читается «указатель на int»).
 
С помощью функции reflect.TypeOf можно вывести типы указателей из приведенной ранее программы:
package main
import (
        "fmt"
        "reflect"
)
func main() {
        var myInt int
        fmt.Println(reflect.TypeOf(&myInt)) //Получает указатель на myInt и выводит тип указателя.
        var myFloat float64
        fmt.Println(reflect.TypeOf(&myFloat)) //Получает указатель на myFloat и выводит тип указателя.
        var myBool bool
        fmt.Println(reflect.TypeOf(&myBool)) //Получает указатель на myBool и выводит тип указателя.*
}
 
 
В программе можно объявлять переменные, содержащие указатели. 
В таких переменных могут храниться только указатели на один конкретный тип переменной, 
так что переменная может содержать только указатели *int, только указатели *float64 и т. д.
package main
import "fmt"
func main() {
        myInt := 4 //Значение
        myIntPointer := &myInt //Указатель
        fmt.Println(myIntPointer) //Выводится сам указатель.
        fmt.Println(*myIntPointer) //Выводится значение, на которое ссылается указатель.
 
        myFloat := 98.6 //Значение
        myFloatPointer := &myFloat //Указатель
        fmt.Println(myFloatPointer) //Выводит сам указатель
        fmt.Println(*myFloatPointer) //Выводится значение, на которое ссылается указатель.
 
        myBool := true //Значение
        myBoolPointer := &myBool     ////Указатель
        fmt.Println(myBoolPointer)   //Выводит сам указатель
        fmt.Println(*myBoolPointer) //Выводится значение, на которое ссылается указатель.
 
 
 
Как и с другими типами, при немедленном присваивании исходного значения переменной-указателю можно воспользоваться коротким объявлением переменной.
package main
import "fmt"
func main() {
        var myBool bool
        myBoolPointer := &myBool //Короткое объявление переменной-указателя.
        fmt.Println(myBoolPointer)
}



Чтение или изменение значения по указателю

!!! Оператор * может использоваться для обновления значения по указателю.
 
package main
import "fmt"
func main() {
        myInt := 4 //Значение
        fmt.Println(myInt) //Вывести значение
        myIntPointer := &myInt //Создаем указатель
        *myIntPointer = 8 //Новое значение присваивается переменной, на которую ссылается указатель (myInt).
        fmt.Println(*myIntPointer) //Выводится значение переменной, на которую ссылается указатель.
        fmt.Println(myInt) //Значение переменной выводится напрямую.
}
 
/*
4 Исходное значение myInt.
8 Результат обновления *myIntPointer.
8 Обновление значения myInt (то же, что *myIntPointer).
*/
 
 
В приведенном коде команда *myIntPointer = 8 обращается к переменной, 
на которую ссылается указатель myIntPointer (то есть переменной myInt) и присваивает ей новое значение. 
Таким образом, обновляется не только значение *myIntPointer, но и myInt


Использование указателей с функциями

Указатели также можно возвращать из функций; 
просто объявите возвращаемый тип функции как тип указателя.
 
Пример кода:
package main
import "fmt"
func createInt() *int {
        var myInt = 100
        return &myInt
}
 
func createPointer() *float64 {
        var myFloat = 98.5
        return &myFloat
}
 
func crS() *string {
        var xX = "hello all"
        return &xX
}
 
 
func main() {
        var myFloatPointer *float64 = createPointer()
        fmt.Println(*myFloatPointer)
 
        var x *int = createInt()
        fmt.Println(*x)
 
        var x1 *string = crS()
        fmt.Println(*x1)
}
 
Кстати говоря, в отличие от некоторых других языков, в Go можно вернуть указатель на переменную, локальную по отношению к функции. 
И хотя эта переменная уже не находится в области видимости, пока у вас есть указатель, Go предоставит вам доступ к значению этой переменной.
package main
import "fmt"
func printPointer(myBoolPointer *bool) {
        fmt.Println(*myBoolPointer)
}
 
func main() {
        var myBool bool = true
        printPointer(&myBool)
}

Рубрики
go

012 / go math rand / Генерация случайного числа

Пакет math/rand содержит функцию Intn, которая сгенерирует случайное число за нас, поэтому в программу следует импортировать math/rand. 
После этого можно будет вызвать функцию rand.Intn для генерирования случайного числа
 
 
 
Пример кода:
package main
import (
        "fmt"
        "math/rand"
)
func main() {
        target := rand.Intn(100) + 1
        fmt.Println(target)
}
Передайте число функции rand.Intn, и функция вернет случайное число в диапазоне от 0 до переданного числа. 
Другими словами, если передать аргумент 100, будет получено случайное число в диапазоне 0–99. 
Так как нам требуется число в диапазоне 1–100, остается прибавить 1 к полученному случайному значению.
Результат сохраняется в переменной target. 
Пока мы ограничимся простым выводом переменной target.
Попытавшись запустить программу в таком виде, вы получите случайное число. 
Однако вы будете получать одно и то же случайное число раз за разом! 
 
Чтобы получать разные случайные числа, необходимо передать значение функции rand.Seed. 
Тем самым вы «инициализируйте» генератор случайных чисел, то есть предоставляете значение, которое будет использоваться для генерирования других случайных чисел.
Но если передавать одно и то же значение инициализации, то и случайные значения будут теми же и мы снова вернемся к тому, с чего начинали.
Ранее было показано, что функция time.Now выдает значение Time, представляющее текущую дату и время. 
Его можно использовать для того, чтобы получать разное значение инициализации при каждом запуске программы.
 
Пример кода:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
seconds := time.Now().Unix()
rand.Seed(seconds)
target := rand.Intn(100) + 1
fmt.Println(target)
}
 
Функция rand.Seed ожидает получить целое число, поэтому передать ей значение Time напрямую не удастся. 
Вместо этого для Time следует вызвать метод Unix, который преобразует его в целое число. 
(А конкретно значение будет преобразовано в формат времени Unix — целое количество секунд, прошедших с 1 января 1970 года. Впрочем, запоминать это не нужно.) 
Это число передается rand.Seed.


Рубрики
go

011 / go if / for / Условия / Циклы

Выражение вычисляется, и если полученный результат равен true, то выполняется код в теле условного блока. 
Если же результат равен false, условный блок пропускается.
 
if true {
fmt.Println("I'll be printed!")
}
 
if false {
fmt.Println("I won't!")
}
 
Как и многие другие языки, Go поддерживает множественное ветвление в условных командах.
Такие команды записываются в форме if...else if...else.
 
if grade == 100 {
fmt.Println("Perfect!")
} else if grade >= 60 {
fmt.Println("You pass.")
} else {
fmt.Println("You fail!")
}
 
Условные команды используют логическое выражение (результат которого равен true или false), 
чтобы определить, должен ли выполняться содержащийся в них код.
if 1 == 1 {
fmt.Println("I'll be printed!")
}
if 1 >= 2 {
fmt.Println("I won't!")
}
if 1 > 2 {
fmt.Println("I won't!")
}
if 2 <= 2 {
fmt.Println("I'll be printed!")
}
if 1 < 2 {
fmt.Println("I'll be printed!")
}
if 2 != 2 {
fmt.Println("I won't!")
}
 
Если код должен выполняться только в том случае, когда условие дает результат false, используйте ! — оператор логического отрицания. 
Этот оператор берет значение true и превращает его в false или же берет значение false и превращает его в true.
if !true {
fmt.Println("I won't be printed!")
}
if !false {
fmt.Println("I will!")
}
 
Если код должен выполняться только в том случае, когда истинны оба условия, используйте оператор && («и»). 
А если он должен выполняться лишь тогда, когда истинно хотя бы одно из двух условий, используйте оператор || («или»).
if true && true {
fmt.Println("I'll be printed!")
}
if false || true {
fmt.Println("I'll be printed!")
}
if true && false {
fmt.Println("I won't!")
}
if false || false {
fmt.Println("I won't!")
}
 
В: Другой язык программирования требует, чтобы условие команды if заключалось в круглые скобки. В Go это не обязательно?
О: Нет. Более того, команда go fmt удалит все круглые скобки, добавленные вами, если только они не используются для определения порядка операций.

Циклы / Цикл for

Пример кода:
package main
import "fmt"
func main() {
        for x := 4; x <= 6; x++ {
                fmt.Println("x is now", x)
        }
}
 
 
Разберем:
for x := 4; x <= 6; x++ {
 fmt.Println("x is now", x)
}
 
for - ключевое слово
x:=4 - команда инициализации
x < 6 - Условное выражение
x++ - Операция приращения
{ - начало блока цикла
}конец блока цикла
fmt.Println("x is now", x) - тело блока цикла
 
Циклы всегда начинаются с ключевого слова for. В одной из стандартных разновидностей циклов за for следуют три сегмента кода, которые управляют циклом:
- Команда инициализации, обычно используемая для инициализации переменной.
- Условное выражение, которое определяет, когда следует прервать выполнение цикла.
- Оператор приращения, который выполняется после каждой итерации цикла.
 
Команда инициализации часто используется для инициализации переменной; 
условное выражение обеспечивает выполнение цикла до того, как переменная достигнет определенного значения, и оператор приращения обновляет значение этой переменной. 
Например, в приведенном фрагменте переменная t инициализируется значением 3, условие обеспечивает выполнение цикла, пока t > 0, 
а оператор приращения уменьшает t на 1 при каждом выполнении цикла. 
В конечном итоге t уменьшается до 0 и цикл завершается.
 
package main
import "fmt"
func main() {
 
        for t := 3; t > 0; t-- {
                fmt.Println(t)
        }
        fmt.Println("Blastoff!")
}
 
 
Операторы ++ и -- часто встречаются в командах приращения циклов.
++ увеличивает значение переменной на 1, а -- уменьшает его на 1.
Примеры кода:
x := 0
x++
fmt.Println(x)
x++
fmt.Println(x)
x--
fmt.Println(x)
 
for x := 1; x <= 3; x++ {
fmt.Println(x)
}
 
for x := 3; x >= 1; x-- {
fmt.Println(x)
}
 
 
В языке Go также поддерживаются операторы присваивания += и -=. 
Они получают значение в переменной, добавляют или вычитают другое значение, а затем присваивают результат той же переменной.
Примеры кода:
x := 0
x += 2
fmt.Println(x)
x += 5
fmt.Println(x)
x -= 3
fmt.Println(x)
 
Операторы += и -= также могут использоваться в циклах для изменения переменной на величину, отличную от 1.
for x := 1; x <= 5; x += 2 {
fmt.Println(x)
}
 
for x := 15; x >= 5; x -= 5 {
fmt.Println(x)
}
 
 
Когда цикл завершается, выполнение программы продолжится с команды, следующей за блоком цикла. 
При этом цикл продолжает выполняться, пока условное выражение остается истинным. 
Этот факт может иметь нежелательные последствия; 
ниже приведены примеры циклов, которые выполняются бесконечно или не выполняются ни одного раза:
 
бесконечный цикл:
for x := 1; true; x++ {
fmt.Println(x)
}
 
Цикл не выполнится никогда:
for x := 1; false; x++ {
fmt.Println(x)
}
 
 
Будьте осторожны!
Цикл может выполняться бесконечно.
В этом случае ваша программа никогда не остановится сама.
Если это случится, в активном окне терминала нажмите клавишу Control одновременно с клавишей C, чтобы прервать выполнение программы.
 
 
Операторы инициализации и приращения необязательны.
При желании операторы инициализации и приращения в заголовке цикла for можно опустить, 
оставив только условное выражение (хотя вы должны проследить за тем, чтобы условие в какой-то момент становилось ложным, иначе в программе возникнет бесконечный цикл).
 
x := 1
for x <= 3 {
fmt.Println(x)
x++
}
 
x := 3
for x >= 1 {
fmt.Println(x)
x--
}

Пропуск частей цикла командами continue и break

В Go предусмотрены два ключевых слова для управления циклом. 
Первое — continue — осуществляет немедленный переход к следующей итерации цикла; 
при этом дальнейший код текущей итерации в блоке цикла пропускается. 
for x := 1; x <= 3; x++ {
fmt.Println("before continue")
continue
fmt.Println("after continue")
}
В приведенном выше примере строка "after continue" никогда не выводится, 
потому что ключевое слово continue всегда выполняет переход к началу цикла — до того, как отработает второй вызов Println.
 
 
Второе ключевое слово break приводит к немедленному выходу из цикла. Дальнейший код в блоке цикла не отрабатывается, другие итерации цикла не выполняются. 
Управление передается первой команде, следующей за циклом.
for x := 1; x <= 3; x++ {
fmt.Println("before break")
break
fmt.Println("after break")
}
fmt.Println("after loop")
Здесь при первой итерации цикла выводится сообщение "before break", после чего команда break немедленно прерывает цикл;
сообщение "after break" не выводится, и цикл не выполняется повторно (хотя без break он бы выполнился еще два раза).
Управление передается команде, следующей за циклом.



Рубрики
go

010 / go Комментарии

Самая распространенная форма комментариев обозначается двумя слешами (//).
Все символы от // до конца строки рассматриваются как часть комментария.
Комментарий // может занимать всю строку или следовать после кода.
 
// Общее количество виджетов в системе.
var TotalCount int // Должно быть целым числом.
 
Более редкая форма комментариев занимает несколько строк. 
Блочные комментарии начинаются с /* и заканчиваются */, а весь текст между этими маркерами (включая символы новой строки) является частью комментария.
/*
Пакет widget включает все функции,
используемые для работы с виджетами.
*/

Рубрики
go

009 / go strings / строки

Пакет strings содержит тип Replacer, который ищет подстроку в строке и заменяет каждое вхождение этой подстроки в другой строке. 
 
Следующий код заменяет каждый символ # в строке буквой o:
package main
import (
        "fmt"
        "strings"
)
func main() {
        broken := "G# r#cks!"
        replacer := strings.NewReplacer("#", "o")
        fixed := replacer.Replace(broken)
        fmt.Println(fixed)
}
Функция strings.NewReplacer получает аргументы — заменяемую строку ("#") и заменяющую строку ("o") — и возвращает значение strings.Replacer. 
Когда мы передаем строку методу Replace значения Replacer, то метод возвращает строку, в которой выполнена указанная замена
 
офф топ:
Значение. Имя метода.
   |          |
replacer.Replace(broken)
      now.Year()
       |   |
Значение. Имя метода.
Рубрики
go

008 / go var / Объявление переменных / Нулевые значения / Короткие объявления переменных / преобразования

В языке Go переменная представляет собой область памяти, в которой хранится значение.
Чтобы к переменной можно было обращаться по имени, используйте объявление переменной. 
Объявление состоит из ключевого слова var, за которым следует имя и тип значений, которые будут храниться в переменной.
 
var quantity int
var length, width float64
var customerName string
 
После того как переменная будет объявлена, ей можно будет присвоить любое значение этого типа оператором = (один знак равенства):
quantity = 2
customerName = "Damon Cole"
 
В одной команде можно присвоить значения сразу нескольким переменным.
Для этого перечислите имена переменных слева от = и такое же количество значений в правой части, разделяя их запятыми.
length, width = 1.2, 2.4
 
После того как переменной будет присвоено значение, вы сможете использовать ее в любом контексте, где может использоваться исходное значение:
package main
import "fmt"
func main() {
var quantity int
var length, width float64
var customerName string
quantity = 4
length, width = 1.2, 2.4
customerName = "Damon Cole"
fmt.Println(customerName)
fmt.Println("has ordered", quantity, "sheets")
fmt.Println("each with an area of")
fmt.Println(length*width, "square meters")
}
 
Пример 2:
package main
import "fmt"
func main() {
        var x1 int
        var x2 int
        var sumx int
        x1 = 500
        x2 = 600
        sumx = x1+x2
        fmt.Println(x1, "+", x2, "=", sumx)
}
 
Если значение переменной известно заранее, можно объявить переменную и присвоить ей значение в одной строке:
var quantity int = 4
var length, width float64 = 1.2, 2.4
var customerName string = "Damon Cole"
 
Существующим переменным можно присваивать новые значения, но эти значения должны относиться к тому же типу. 
Статическая типизация в Go гарантирует, что переменной не будет случайно присвоено значение неподходящего типа.
 
Если значение переменной присваивается одновременно с ее объявлением, тип переменной в объявлении обычно не указывают. 
Тип значения, присвоенного переменной, будет считаться типом этой переменной.
 
var quantity = 4
var length, width = 1.2, 2.4
var customerName = "Damon Cole"
fmt.Println(reflect.TypeOf(quantity))
fmt.Println(reflect.TypeOf(length))
fmt.Println(reflect.TypeOf(width))
fmt.Println(reflect.TypeOf(customerName))
 
Пример:
package main
import "fmt"
func main() {
        var x1 int = 700
        var x2 int = 800
        var sumx  = x1+x2
        fmt.Println(x1, "+", x2, "=", sumx)
}



Нулевые значения

Если переменная объявляется без присваивания значения, то она будет содержать нулевое значение для этого типа. 
Для числовых типов нулевое значение равно 0.
Пример:
package main
import "fmt"
func main() {
        var myInt int
        var myFloat float64
        fmt.Println(myInt, myFloat)
}
 
 
 
Но для других типов значение 0 будет недействительным, поэтому нулевое значение для этого типа будет отличаться. 
Скажем, для строковых переменных нулевым значением является пустая строка, а для переменных bool — значение false.
Пример:
package main
import "fmt"
func main() {
        var myString string
        var myBool bool
        fmt.Println(myString, myBool)
}

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

Вместо того чтобы явно объявлять тип переменной и позднее присваивать ей значение оператором =, 
вы совмещаете эти две операции с помощью синтаксиса :=.
 
Обычное объявление переменной выглядит вот так:
var quantity int = 4 
var length, width float64 = 1.2, 2.4 
var customerName string = "Damon Cole"
 
Короткие объявления переменных выглядит так:
quantity := 4 
length, width := 1.2, 2.4 
customerName := "Damon Cole"
 
 
Пример в коде:
package main
import "fmt"
func main() {
        quantity := 4
        length, width := 1.2, 2.4
        customerName := "Damon Cole"
        fmt.Println(customerName)
        fmt.Println("has ordered", quantity, "sheets")
        fmt.Println("each with an area of")
        fmt.Println(length*width, "square meters")
}
 
Явно объявлять тип переменной не обязательно: 
тип значения, 
присвоенного переменной, 
становится типом этой переменной.
 
Поскольку короткие объявления переменных очень удобны и компактны, они используются чаще обычных объявлений.
Впрочем, время от времени вам будут встречаться обе формы, поэтому важно знать их.

Правила выбора имен для переменных

В Go существует один простой набор правил, применяемых к именам переменных, функций и типов:
 
!!! Эти правила должны обязательно выполняться на уровне языка
00. Имя должно начинаться с буквы и может содержать любое количество дополнительных букв и цифр.
 
01. Если имя переменной, функции или типа начинается с буквы верхнего регистра, оно считается экспортируемым и может использоваться в других пакетах, кроме текущего. 
(Именно поэтому буква P в fmt.Println имеет верхний регистр: 
это нужно для того, чтобы его можно было использовать в main или любом другом пакете.) 
Если имя переменной/функции/типа начинается с буквы нижнего регистра, оно считается не экспортируемым. 
Такие имена доступны только в текущем пакете.
 
!!! Но сообщество Go также соблюдает ряд дополнительных соглашений:
02. Если имя состоит из нескольких слов, каждое слово после первого должно начинаться с буквы верхнего регистра, 
и они должны следовать друг за другом без разделения пробелами: topPrice, RetryConnection и т. д. 
(Первая буква имени имеет верхний регистр только в том случае, если оно должно экспортироваться из пакета.) 
Этот стиль записи часто называется верблюжьим регистром, потому что буквы верхнего регистра напоминают горбы у верблюда.
 
03. Если смысл имени очевиден по контексту, в сообществе Go принято сокращать его: использовать i вместо index, max вместо maximum и т. д.
 
Примеры:
 
Нормально:
sheetLength
TotalUnits
i
 
нарушают соглашения:
sheetlength - Остальные слова должны начинаться с буквы верхнего регистра!
Total_Units - Допустимо, но слова должны записываться подряд!
index - Хорошо бы заменить сокращением!
 
 
!!!
Только переменные, функции и типы, имена которых начинаются с буквы верхнего регистра, 
считаются экспортируемыми, то есть доступными за пределами текущего пакета.




Преобразование переменных

Пример не рабочего кода:
package main
import "fmt"
func main() {
        var length float64 = 1.2
        var width int = 2
        fmt.Println("Area is", length*width)
        fmt.Println("length > width?", length > width)
}
 
Исправим код:
package main
import "fmt"
func main() {
        var length float64 = 1.2
        var width int = 2
        fmt.Println("Area is", length*float64(width))
        fmt.Println("length > width?", length > float64(width))
}
Теперь математические операции и операции сравнения работают правильно!
 
 
Попробуем преобразовать значение int к типу float64 перед присваиванием переменной float64:
var length float64 = 1.2
var width int = 2
length = float64(width) - Преобразование int к типу float64 перед присваиванием переменной float64
fmt.Println(length)
 
 
Всегда держите в голове, как преобразования изменяют выходные значения. 
Например, переменные float64 могут хранить дробные значения, а переменные int — нет. 
Когда вы преобразовываете float64 в int, дробная часть просто отбрасывается! 
Это может внести путаницу в любые операции, выполняемые с результирующим значением.
var length float64 = 3.75
var width int = 5
width = int(length) - В результате этого преобразования дробная часть теряется!
fmt.Println(width)
 
Но если действовать внимательно, вы поймете, что преобразования исключительно важны для работы с Go. 
С ними вы сможете совместно использовать несовместимые типы.
 
Пример:
var price int = 100
var taxRate float64 = 0.08
var tax float64 = float64(price) * taxRate
fmt.Println(tax)


Рубрики
go

007 / go mat / Числа / Математические операции и сравнения / типы значений

Числа

Числа тоже можно определять прямо в программном коде. 
Это еще проще, чем определять строковые литералы: просто введите нужное число.
 
Как вы вскоре увидите, в языке Go целые числа и числа с плавающей точкой интерпретируются как разные типы. 
Помните, что целое число можно отличить от числа с плавающей точкой по разделителю дробной части — точке.
 
Пример:
42 Целое число. int
3.1415 Число с плавающей точкой float
 
Пример в коде:
package main
import "fmt"
func main() {
fmt.Println(0, 3.1415)
}

Математические операции и сравнения

Основные математические операторы Go работают так же, как и в большинстве других языков. 
оператор + выполняет сложение
оператор - выполняет вычитание
оператор * — умножение
оператор / — деление
 
Пример кода:
package main
import "fmt"
func main() {
        fmt.Println("t1 + 2 =", 1+2, "nt5.4 - 2.2 =", 5.4-2.2, "nt3 * 4 =", 3*4, "nt7.5 / 5 =", 7.5/5)
}
 
 
 
При помощи операторов < и > можно сравнить два значения и проверить, какое из них больше другого. 
оператор == (два знака равенства) проверяет, что два значения равны
оператор != проверяет, что два значения не равны
оператор <= проверяет, что второе значение меньше или равно первому
оператор >= проверяет, что второе значение больше или равно первому
Результатом сравнения является логическое значение (true или false)
 
package main
import "fmt"
func main() {
        fmt.Println("t4 < 6 ttt", 4 < 6, "nt4 > 6 ttt", 4 > 6, "nt2 + 2 == 5 tt", 2+2 == 5, "nt2 + 2 !=5 tt", 2+2 != 5, "nt4 < = 6 tt", 4 <= 6, "nt4 >= 4ttt", 4 >= 4)
}

Узнаем типы значений

Чтобы узнать тип любого значения, передайте его функции TypeOf из пакета reflect. 
Давайте узнаем типы некоторых значений, которые уже встречались в примерах программ:
package main
import (
        "fmt"
        "reflect"
)
func main() {
        fmt.Println(reflect.TypeOf(42))
        fmt.Println(reflect.TypeOf(3.1234))
        fmt.Println(reflect.TypeOf(true))
        fmt.Println(reflect.TypeOf("Hello world"))
}
 
Типы:
int     Целое число (не имеющее дробной части)
float64 Число с плавающей точкой. 
        Тип используется для хранения чисел, имеющих дробную часть. 
        (Для хранения числа используются 64 бита данных, отсюда суффикс 64 в имени. 
        Значения типа float64 обеспечивают очень высокую, хотя и не бесконечную точность.)
bool    Логическое значение (true или false)
string  Строка— последовательность данных, которые обычно представляют символы текста

Рубрики
go

004 / go Display image

package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"image"
	"image/png"
)

var favicon = []byte{
	0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
	0x10, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x5d, 0x52, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x50,
	0x4c, 0x54, 0x45, 0x7a, 0xdf, 0xfd, 0xfd, 0xff, 0xfc, 0x39, 0x4d, 0x52, 0x19, 0x16, 0x15, 0xc3, 0x8d, 0x76, 0xc7,
	0x36, 0x2c, 0xf5, 0x00, 0x00, 0x00, 0x40, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x95, 0xc9, 0xd1, 0x0d, 0xc0, 0x20,
	0x0c, 0x03, 0xd1, 0x23, 0x5d, 0xa0, 0x49, 0x17, 0x20, 0x4c, 0xc0, 0x10, 0xec, 0x3f, 0x53, 0x8d, 0xc2, 0x02, 0x9c,
	0xfc, 0xf1, 0x24, 0xe3, 0x31, 0x54, 0x3a, 0xd1, 0x51, 0x96, 0x74, 0x1c, 0xcd, 0x18, 0xed, 0x9b, 0x9a, 0x11, 0x85,
	0x24, 0xea, 0xda, 0xe0, 0x99, 0x14, 0xd6, 0x3a, 0x68, 0x6f, 0x41, 0xdd, 0xe2, 0x07, 0xdb, 0xb5, 0x05, 0xca, 0xdb,
	0xb2, 0x9a, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
}

// displayImage renders an image to the playground's console by
// base64-encoding the encoded image and printing it to stdout
// with the prefix "IMAGE:".
func displayImage(m image.Image) {
	var buf bytes.Buffer
	err := png.Encode(&buf, m)
	if err != nil {
		panic(err)
	}
	fmt.Println("IMAGE:" + base64.StdEncoding.EncodeToString(buf.Bytes()))
}

func main() {
	m, err := png.Decode(bytes.NewReader(favicon))
	if err != nil {
		panic(err)
	}
	displayImage(m)
}
Рубрики
go

003 / go clear screen

package main

import (
	"fmt"
	"strings"
	"time"
)

func main() {
	const col = 30
	// Clear the screen by printing x0c.
	bar := fmt.Sprintf("x0c[%%-%vs]", col)
	for i := 0; i < col; i++ {
		fmt.Printf(bar, strings.Repeat("=", i)+">")
		time.Sleep(100 * time.Millisecond)
	}
	fmt.Printf(bar+" Done!", strings.Repeat("=", col))
}
[==============================] Done!
Рубрики
go

006 / go rune / Руны

Если строки обычно используются для представления последовательностей символов, 
то руны в языке Go представляют отдельные символы.
Строковые литералы заключаются в двойные кавычки ("),
а рунные литералы записываются в одиночных кавычках (').
 
В программах Go могут использоваться практически любые символы любых мировых языков, потому что в Go для хранения рун используется стандарт Юникод. 
Руны хранятся в виде числовых кодов, а не в виде символов; если передать руну функции fmt.Println, то выведется числовой код, а не исходный символ.
В рунных литералах (как и в строковых) можно использовать служебные последовательности для представления символов, которые неудобно вводить с клавиатуры для включения в программу.
'A' - 65
'B' - 66
'Ж' - 1174
't' - 9
'n' - 10 
'' - 92
 
Пример кода:
package main
 
import "fmt"
 
func main() {
        fmt.Println('A', 'B', 'Ж', 't', 'n', '')
}

Рубрики
go

005 / go hello

Структура типичного файла Go
1. Директива package.         package main
2. Директива import.          import "fmt"
3. Собственно код программы.  function main{}

Пример:
package main
import "fmt"
func main () {
fmt.Println("Hello, Go")
}


vim test.go
-----------
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
-----------
Проверка:
Производим форматирование скрипта
go fmt test.go
Производим тестовый запуск 
go run test.go
Производим сборку
go build test.go
Проверяем еще раз работоспособность 
Рубрики
go

002 / go fibonacci

package main

import "fmt"

// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

func main() {
	f := fib()
	// Function calls are evaluated left-to-right.
	fmt.Println(f(), f(), f(), f(), f(), f(), f(), f())
}

Рубрики
go

001 / go fmt.Printf() / fmt.Println()

Printf

Основные спецификаторы формата (%)
Вот наиболее часто используемые значения для % в fmt.Printf:

Общие спецификаторы:
%v — Выводит значение в стандартном формате.
%+v — Добавляет имена полей структуры.
%#v — Отображает значение в виде синтаксиса Go (например, для отладки).
%T — Тип значения.
%% — Выводит %.

Числа:
%b — Двоичное представление числа.
%c — Unicode-символ, соответствующий числу.
%d — Десятичное целое число.
%o — Восьмеричное представление.
%x, %X — Шестнадцатеричное представление (строчные и заглавные буквы соответственно).
%e, %E — Научный формат.
%f, %F — Десятичное представление числа с плавающей запятой.
%g, %G — Наиболее компактное представление числа (выбирает между %f и %e).

Строки и символы:
%s — Строка.
%q — Строка в кавычках.
%x — Строка в виде шестнадцатеричного кода.
%p — Указатель (адрес в памяти).
Логические значения:
%t — Выводит true или false.


Пример использования:
package main

import "fmt"

func main() {
    n := 42
    s := "Hello, Go!"
    f := 3.14159
    b := true

    fmt.Printf("Число: %dn", n)         // Число: 42
    fmt.Printf("Строка: %sn", s)        // Строка: Hello, Go!
    fmt.Printf("Плавающая точка: %fn", f) // Плавающая точка: 3.141590
    fmt.Printf("Логическое: %tn", b)    // Логическое: true
    fmt.Printf("Тип переменной n: %Tn", n) // Тип переменной n: int
    fmt.Printf("n в двоичном формате: %bn", n) // n в двоичном формате: 101010
    fmt.Printf("Now you have %g problems.n", math.Sqrt(7555))
}

Println


В аргументах Println передавались строки. 
Строка представляет собой последовательность байтов, которые обычно представляют символы текста. 
Строки можно определять прямо в программе в виде строковых литералов: 
компилятор Go интерпретирует текст, заключенный в двойные кавычки, как строку
 
Открывающая двойная кавычка "Hello, Go!" Закрывающая двойная кавычка
Результат: Hello, Go!
 
Некоторые управляющие символы, которые неудобно вводить с клавиатуры (символы новой строки, табуляции и т. д.), 
внутри строк могут представляться в виде служебных последовательностей: символа «обратный слеш», за которым следует другой символ (или символы).
 
n Символ новой строки
t Символ табуляции
" Двойная кавычка
 Обратный слеш
 
Пример 0:
"Hello,nGo!" 
Результат:
Hello,
Go!
 
Пример 1:
"Hello, tGo!"
Результат:
Hello,    Go!
 
Пример 2:
""Hello, Go!""
"Hello, Go!"
Рубрики
go

000 / go install and build / Установка golang на linux / сборка

Установка golang на linux

0. Установка
# debian  ubuntu
apt update && apt upgrade && apt install golang
# archlinux
pacman -Sy go 

1. Компиляция и запуск файлов
Go — это компилируемый язык. 

Первый способ — через команду "go run". 
Эта команда компилирует исполняемый файл, запускает его и удаляет. 
Этим способом пользуются, когда нужно разово запустить небольшую программу на Go и забыть.

Второй способ — через команду "go build". 
Она выполняет компиляцию и создает исполняемый файл в текущей директории.

go build hello.go # Создаем бинарный файл hello.go
В директории с файлом должен появиться новый файл hello. 
Можно запустить его как обычный исполняемый файл:
./hello

Пример:
go fmt hello-world.go
go build hello-world.go
go run build hello-world.go

Кросс компиляция под разные платформы:

go mod init hello # Создаем Go-модуль
GOOS=windows GOARCH=386 go build -o hello_windows.exe # Исполняемый файл для Windows
GOOS=windows GOARCH=amd64 go build -o hello_windows64.exe # Файл для Windows с архитектурой x64
GOOS=linux GOARCH=amd64 go build -o hello_amd64 # Файл для linux с архитектурой x64
GOOS=linux GOARCH=arm go build -o hello_linux_arm # Файл для Linux на arm
GOOS=darwin GOARCH=arm64 go build -o hello_mac_arm64 # для mac os

Показать переменные go / рабочее пространство

go env
 
В языке Go поиск программ и их зависимостей (например, import "пакет"), сначала выполняется в каталогах, 
прописанных в переменную $GOPATH, а затем - в переменной $GOROOT (путь установки go, по умолчанию /usr/lib/go).
 
 
$GOPATH работает как $PATH и может содержать несколько записей. 
Это может быть полезно для отделения пакетов, скачанных через go get, от вашего кода, например GOPATH=$HOME/go:$HOME/mygo
 
Создать само рабочее пространство:
$ mkdir -p ~/go/src
каталог ~/go/src предназначен для хранения исходных текстов проектов. 
При компиляции Go также создаст каталог bin для исполняемых файлов и pkg для кэша отдельных пакетов.
Вы можете добавить ~/go/bin в переменную окружения $PATH для запуска установленных Go-программ:
 
export PATH="$PATH:$HOME/go/bin"

Рубрики
go

visual code / VS / Code

Плагины:

Beetter Comments
Code Spell Checker 
Error Lens
GitLens - Git supercharged
go
indent-rainbow
Markdown All in One
markdownllint
Russian - Code Spell Checker - В настройках нужно включить
ShellCheck
Todo Tree

conten markdown

Ctrl + Shift + P
content

Плагины для golang

создайте пустую папку и в ней файл main.go
и vs предложит установить

config

cat .vscode/settings.json
-------------------------
{
  // - // cursor
  "editor.cursorBlinking": "expand",
  "editor.cursorStyle": "line",
  "editor.cursorWidth": 2,
  "editor.cursorSmoothCaretAnimation": "explicit",
  // - // scrol bar
  "editor.smoothScrolling": true,
  "editor.scrollbar.horizontal": "hidden",
  "editor.scrollbar.vertical": "hidden",
  "editor.minimap.enabled": true,
  "editor.minimap.maxColumn": 200,
  "editor.minimap.size": "proportional",
  "editor.scrollBeyondLastLine": true,
  // - // TAB
  "editor.tabSize": 4,
  "editor.detectIndentation": true,
  // - // font
  "editor.fontFamily": "'Droid Sans Mono', 'monospace', monospace",
  "editor.fontSize": 14,
  "editor.fontLigatures": false,
  "editor.lineHeight": 0,
  "window.zoomLevel": 0,
  // - // wrap
  "editor.rulers": [
    90
  ],
  "editor.wordWrap": "bounded",
  "editor.wordWrapColumn": 90,
  //"editor.rulers": [],
  //"editor.wordWrap": "off",
  // - // auto closing tag
  "javascript.autoClosingTags": true,
  "html.autoClosingTags": true,
  "typescript.autoClosingTags": true,
  // - // hint
  "editor.quickSuggestionsDelay": 0,
  // - // terminal
  "terminal.integrated.fontFamily": "",
  "terminal.integrated.fontSize": 14,
  "terminal.integrated.tabs.enabled": false,
  // - // folder
  "explorer.autoOpenDroppedFile": false,
  "explorer.confirmDelete": true,
  "explorer.compactFolders": false,
  "workbench.editor.tabSizing": "shrink",
  // - // debug
  "debug.toolBarLocation": "hidden",
  "debug.focusWindowOnBreak": false,
  "debug.showInlineBreakpointCandidates": false,
  "debug.showBreakpointsInOverviewRuler": false,
  // -  // over
  "editor.formatOnSave": true,
  "editor.renderWhitespace": "boundary",
  "editor.insertSpaces": true,
  "editor.trimAutoWhitespace": true,
  "files.encoding": "utf8",
  "files.eol": "auto",
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true,
  "files.trimFinalNewlines": true,
  "workbench.list.smoothScrolling": true,
  "workbench.editor.closeOnFileDelete": true,
  "workbench.editor.highlightModifiedTabs": true,
  "editor.quickSuggestions": {
    "strings": true
  },
  "commentTranslate.targetLanguage": "ru",
  "[json]": {
    "editor.defaultFormatter": "vscode.json-language-features",
  },
  "[go]": {
    "editor.insertSpaces": false,
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": "always"
    },
    "editor.defaultFormatter": "golang.go"
  },
  "[markdown]": {
    "editor.defaultFormatter": "DavidAnson.vscode-markdownlint",
    "editor.formatOnSave": true
  },
  "todo-tree.general.tagGroups": {
    "BUG": [
      "BUG",
      "ERROR",
      "ERR",
      "ISSUE",
      "PROBLEM"
    ],
    "HACK": [
      "HACK",
      "HACKY",
      "HACKED",
      "HACKING",
      "HACKS"
    ],
    "FIX": [
      "FIXME",
      "FIXIT",
      "FIX IT",
      "FIX"
    ],
    "TODO": [
      "TODO",
      "TO DO",
      "TO-DO",
      "TASK",
      "TASKS",
      "DO IT",
      "DOIT"
    ],
    "IDEA": [
      "IDEA",
      "IDEAS"
    ]
  },
  "todo-tree.filtering.excludeGlobs": [
    "**/node_modules/*/**",
    "**/dist/*/**",
    "**/.vscode/**",
    "**/.git/**",
    "**/.github/**",
    ".gitignore",
    "package-lock.json"
  ],
  "todo-tree.tree.hideTreeWhenEmpty": true,
  "todo-tree.regex.regex": "(//|#|