воскресенье, 31 мая 2020 г.

Работа с базами данных в Golang. Пакет database/sql в Golang

Пакет database/sql предоставляет общий интерфейс для баз данных SQL (или SQL-подобных).

Драйвер базы данных

Пакет database/sql должен использоваться вместе с драйвером базы данных.

В приведенном ниже описании предполагается, что драйвер был импортирован.

Подключение к базе данных

Open используется для создания дескриптора базы данных:

db, err := sql.Open(driver, dataSourceName)

Где driver указывает драйвер базы данных, а dataSourceName указывает информацию о подключении к базе данных, такую ​​как имя базы данных и учетные данные для аутентификации.

Обратите внимание, что Open не открывает соединение с базой данных напрямую: это откладывается до выполнения запроса. Чтобы убедиться, что соединение может быть установлено перед выполнением запроса, используйте метод PingContext:

if err := db.PingContext(ctx); err != nil {
    log.Fatal(err)
}

После использования база данных закрывается с помощью метода Close.

Выполнение запросов

ExecContext используется для запросов, где строки ответа (rows) не возвращаются:

result, err := db.ExecContext(ctx,
    "INSERT INTO users (name, age) VALUES ($1, $2)",
    "gopher",
    27,
)

Где result содержит идентификатор последней вставки и количество затронутых строк. Доступность этих значений зависит от драйвера базы данных.

QueryContext используется для поиска:

rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age = $1", age)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    var name string
    if err := rows.Scan(&name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}

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

var age int64
err := db.QueryRowContext(ctx, "SELECT age FROM users WHERE name = $1", name).Scan(&age)

Подготовленные утверждения (statements) могут быть созданы с PrepareContext:

age := 27
stmt, err := db.PrepareContext(ctx, "SELECT name FROM users WHERE age = $1")
if err != nil {
    log.Fatal(err)
}
rows, err := stmt.Query(age)
// обработка строк ответа

ExecContext, QueryContext и QueryRowContext могут быть вызваны для утверждений (statements). После использования утверждение должно быть закрыто с помощью Close.

Транзакции

Транзакции начинаются с BeginTx:

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    log.Fatal(err)
}

Уже описанные методы ExecContext, QueryContext, QueryRowContext и PrepareContext могут использоваться в транзакции.

Транзакция должна завершиться вызовом Commit или Rollback.

Работа с NULL

Если столбец базы данных обнуляется, один из типов, поддерживающих нулевые значения, должен быть передан в Scan.

Например, если столбец name в таблице names обнуляется:

var name sql.NullString
err := db.QueryRowContext(ctx, "SELECT name FROM names WHERE id = $1", id).Scan(&name)
...
if name.Valid {
    // используем name.String
} else {
    // значение равно NULL
}

В database/sql реализованы только NullBool, NullFloat64, NullInt64, NullInt32, NullString и NullTime. Реализации специфических для базы данных нулевых типов оставлены для драйвера базы данных. Пользовательские типы, поддерживающие NULL, могут быть созданы путем реализации интерфейсов database/sql/driver.Valuer и database/sql.Scanner.


Читайте также:


пятница, 29 мая 2020 г.

Использовать sync.Mutex или канал?

Один из девизов Go: "Не общайтесь, разделяя память; делитесь памятью, общаясь".

Тем не менее, Go предоставляет традиционные механизмы блокировки в пакете sync. Большинство проблем с блокировкой можно решить с помощью каналов или традиционных блокировок.

Так что вы должны использовать?

Используйте то, что наиболее выразительно и/или наиболее просто.

Распространенной ошибкой новичка в Go является чрезмерное использование каналов и goroutine только потому, что это возможно, и/или потому, что это весело. Не бойтесь использовать sync.Mutex, если это лучше всего подходит для вашей проблемы. Go прагматичен, позволяя вам использовать инструменты, которые наилучшим образом решают вашу проблему, и не заставляет вас использовать один стиль кода.

Как общее руководство, однако:

Канал Мьютекс
передача права собственности на данные, раздача единиц работы, передача асинхронных результатов кэши, состояние

Если вы когда-нибудь обнаружите, что правила блокировки sync.Mutex становятся слишком сложными, спросите себя, может ли быть проще использовать канал(ы).

Группа ожидания (sync.WaitGroup)

Другим важным примитивом синхронизации является sync.WaitGroup. Он позволяет взаимодействующим группам совместно ожидать пороговое событие, прежде чем продолжить самостоятельно. Это полезно, как правило, в двух случаях.

Во-первых, при "очистке" можно использовать sync.WaitGroup, чтобы гарантировать, что все goroutines, включая основную (main), ожидают, пока все не завершатся чисто.

Второй более общий случай - это циклический алгоритм, который включает набор процедур, которые некоторое время работают независимо, затем все ждут на барьере, прежде чем продолжить самостоятельно. Этот шаблон может повторяться много раз. Данные могут быть обменены на барьерном событии. Эта стратегия является основой Массового Синхронного Параллелизма (BSP, Bulk Synchronous Parallelism).

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


Читайте также:


Пакет flag в Golang

Пакет flag реализует синтаксический анализ флага командной строки.

Применение

Определите флаги, используя flag.String(), Bool(), Int() и т. д.

Здесь объявляется целочисленный флаг -flagname, хранящийся в указателе ip, с типом *int.

import "flag"
var ip = flag.Int("flagname", 1234, "вспомогательное сообщение для flagname")

При желании вы можете привязать флаг к переменной, используя функции Var().

var flagvar int
func init() {
    flag.IntVar(&flagvar, "flagname", 1234, "вспомогательное сообщение для flagname")
}

Или вы можете создать пользовательские флаги, которые удовлетворяют интерфейсу Value (с получателями указателей), и связать их с анализом флага с помощью

flag.Var(&flagVal, "name", "вспомогательное сообщение для flagname")

Для таких флагов значением по умолчанию является только начальное значение переменной.

После того, как все флаги определены, вызовите

flag.Parse()

чтобы разобрать командную строку в определенные флаги.

Флаги могут быть использованы напрямую. Если вы используете сами флаги, они все являются указателями; если вы привязываетесь к переменным, это значения.

fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)

После синтаксического анализа аргументы, следующие за флагами, доступны как флаг slice.Args() или отдельно как flag.Arg(i). Аргументы индексируются от 0 до flag.NArg()-1.

Синтаксис флага командной строки

Разрешены следующие формы:

-flag
-flag=x
-flag x  // только не булевы флаги

Можно использовать один или два знака минус; они эквивалентны. Последняя форма недопустима для логических флагов, потому что значение команды

cmd -x *

где * - это подстановочный знак оболочки Unix, он изменится, если есть файл с именем 0, false и т. д. Вы должны использовать форму -flag=false, чтобы отключить логический флаг.

Разбор флага останавливается непосредственно перед первым аргументом без флага ("-" является аргументом без флага) или после терминатора "-".

Целочисленные флаги принимают 1234, 0664, 0x1234 и могут быть отрицательными. Булевы флаги могут быть:

1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False

Флаги продолжительности принимают любой ввод, действительный для time.ParseDuration.

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

Пример использования flag

// Эти примеры демонстрируют 
// более сложное использование пакета flag.
package main

import (
    "errors"
    "flag"
    "fmt"
    "strings"
    "time"
)

// Пример 1: одиночный строковый флаг 
// с именем "species" со значением по умолчанию "gopher".
var species = flag.String("species", "gopher", "the species we are studying")

// Пример 2. Два флага, совместно использующих переменную, 
// поэтому мы можем иметь сокращение.
// Порядок инициализации не определен, поэтому убедитесь, 
// что оба используют то же значение по умолчанию. 
// Они должны быть настроены с помощью функции init.
var gopherType string

func init() {
    const (
        defaultGopher = "pocket"
        usage = "the variety of gopher"
    )
    flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
    flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}

// Пример 3: пользовательский тип флага, 
// срез продолжительностей.
type interval []time.Duration

// String - это метод для форматирования значения флага, 
// часть интерфейса flag.Value.
// Вывод метода String будет использоваться в диагностике.
func (i *interval) String() string {
    return fmt.Sprint(*i)
}

// Set - это метод для установки значения флага, 
// часть интерфейса flag.Value.
// Аргументом Set является строка, 
// которая будет проанализирована для установки флага.
// Это список через запятую, поэтому мы разбиваем его.
func (i *interval) Set(value string) error {
    // Если мы хотим разрешить установку флага несколько раз,
    // накапливая значения, мы удалили бы этот оператор if.
    // Это позволило бы использовать такие, как
    // -deltaT 10s -deltaT 15s
    // и другие комбинации.
    if len(*i) > 0 {
        return errors.New("interval flag already set")
    }
    for _, dt := range strings.Split(value, ",") {
        duration, err := time.ParseDuration(dt)
        if err != nil {
            return err
        }
        *i = append(*i, duration)
    }
    return nil
}

// Определяем флаг для накопления длительностей. 
// Потому что у него есть особый тип,
// нам нужно использовать функцию Var 
// и поэтому создавать флаг во время init.

var intervalFlag interval

func init() {
    // Привязать флаг командной строки 
    // к переменной intervalFlag и
    // установить сообщение об использовании.
    flag.Var(&intervalFlag, "deltaT", "разделенный запятыми список интервалов, используемых между событиями")
}

func main() {
    // Все интересные части с переменными, 
    // объявленными выше, но
    // чтобы позволить пакету flag видеть флаги, 
    // определенные там, нужно
    // выполнить, как правило, 
    // в начале main (не init!):
    
    flag.Parse()
}


Читайте также:


четверг, 28 мая 2020 г.

Особенности работы regexp в Golang

Почему регулярное выражение [0-9]*, которое должно соответствовать строке с нулем или более цифрами, соответствует строке с символами в ней?

matched, err := regexp.MatchString(`[0-9]*`, "12three45")
fmt.Println(matched) // true
fmt.Println(err)     // nil (регулярное выражение валидно)

Ответ

Функция regexp.MatchString (как и большинство функций в пакете regexp) выполняет сопоставление подстрок.

Чтобы проверить, соответствует ли полная строка [0-9]*, закрепите начало и конец регулярного выражения:

  • каретка ^ соответствует началу текста или строки,
  • знак доллара $ соответствует концу текста.

matched, err := regexp.MatchString(`^[0-9]*$`, "12three45")
fmt.Println(matched) // false
fmt.Println(err)     // nil (регулярное выражение валидно)


Читайте также:


Нет JSON после маршалинга

Почему json.Marshal создает пустые структуры в текстовом выводе JSON?

type Person struct {
    name string
    age  int
}

p := Person{"Alice", 22}
jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData))

{}

Ответ

В выводе JSON будут присутствовать только экспортированные поля структуры Go.

type Person struct {
    Name string // Изменено на заглавную N
    Age  int    // Изменено на заглавную A
}

p := Person{"Alice", 22}

jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData))

{"Name":"Alice","Age":22}

Вы можете указать имя поля JSON явно с помощью тега json: .

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

p := Person{"Alice", 22}

jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData))

{"name":"Alice","age":22}


Читайте также:


среда, 27 мая 2020 г.

Итерационные переменные и замыкания в Golang

Почему эта программа

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func() {
            fmt.Print(i)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println()
}

печатает

55555

(WaitGroup ожидает завершения набора goroutine.)

Ответ

Существует гонка данных: переменная i совместно используется шестью (6) goroutine.

Гонка данных происходит, когда две goroutine обращаются к одной и той же переменной одновременно, и по крайней мере одно из обращений является записью.

Чтобы избежать этого, используйте локальную переменную и передайте число в качестве параметра при запуске goroutine.

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(n int) { // Используем локальную переменную.
            fmt.Print(n)
            wg.Done()
        }(i)
    }
    wg.Wait()
    fmt.Println()
}

Пример вывода:

40123

Также возможно избежать этой гонки данных, все еще используя замыкание, но тогда мы должны позаботиться об использовании уникальной переменной для каждой goroutine.

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        n := i // Создаем уникальную переменную для каждого замыкания.
        go func() {
            fmt.Print(n)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println()
}


Читайте также:


Переменная итерации не видит изменений в range цикле

Почему переменная итерации x не замечает, что a[1] обновлено?

var a [2]int
for _, x := range a {
    fmt.Println("x =", x)
    a[1] = 8
}
fmt.Println("a =", a)

x = 0
x = 0        <- Почему здесь не 8?
a = [0 8]

Ответ

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

Чтобы избежать копирования массива, итерируйте срез.

var a [2]int
for _, x := range a[:] {
    fmt.Println("x =", x)
    a[1] = 8
}
fmt.Println("a =", a)

x = 0
x = 8
a = [0 8]


Читайте также:


вторник, 26 мая 2020 г.

Изменение записей в range цикле в Golang

Почему срез не обновляется в этом примере?

s := []int{1, 1, 1}
for _, n := range s {
    n += 1
}
fmt.Println(s)
// Вывод: [1 1 1]

Ответ

Range цикл копирует значения из среза в локальную переменную n; обновление n не повлияет на срез.

Обновите записи среза, обращаясь к ним по индексам:

s := []int{1, 1, 1}
for i := range s {
    s[i] += 1
}
fmt.Println(s)
// Вывод: [2 2 2]


Читайте также:


Неожиданные значения в range цикле

Почему этот код

primes := []int{2, 3, 5, 7}
for p := range primes {
    fmt.Println(p)
}

печатает

0
1
2
3

Ответ

Для массивов и срезов range цикл генерирует два значения:

  • сначала индекс,
  • затем данные в этой позиции.

Если вы опустите второе значение, вы получите только индексы.

Чтобы распечатать данные, используйте второе значение:

primes := []int{2, 3, 5, 7}
for _, p := range primes {
    fmt.Println(p)
}

2
3
5
7

В этом случае пустой идентификатор (подчеркивание) используется для возвращаемого значения, которое вас не интересует.


Читайте также:


понедельник, 25 мая 2020 г.

Индекс вне диапазона

Почему происходит сбой этой программы?

a := []int{1, 2, 3}
for i := 1; i <= len(a); i++ {
    fmt.Println(a[i])
}

panic: runtime error: index out of range

Ответ

В последней итерации i равен len(a), которая находится за пределами a.

Массивы, срезы и строки индексируются, начиная с нуля, поэтому значения a находятся в a[0], a[1], a[2], …, a[len(a)-1].

Вместо этого проходите цикл от 0 до len(a)-1.

for i := 0; i < len(a); i++ {
    fmt.Println(a[i])
}

Или, еще лучше, используйте range.

for _, n := range a {
    fmt.Println(n)
}


Читайте также:


Время не число в Golang

Почему не компилируется?

n := 100
time.Sleep(n * time.Millisecond)

invalid operation: n * time.Millisecond (mismatched types int and time.Duration)

Ответ

В Go нет смешивания числовых типов. Вы можете умножить time.Duration только на

  • другой time.Duration или
  • нетипизированную целочисленную константу.

Вот три правильных примера.

var n time.Duration = 100
time.Sleep(n * time.Millisecond)

const n = 100
time.Sleep(n * time.Millisecond)

time.Sleep(100 * time.Millisecond)

Смотрите нетипизированные числовые константы без ограничений для деталей о типизированных и нетипизированных целочисленных и константах с плавающей запятой и их пределах.


Читайте также:


воскресенье, 24 мая 2020 г.

Деление по модулю в Golang

Почему -1 не посчитался нечетным?

func Odd(n int) bool {
    return n%2 == 1
}

func main() {
    fmt.Println(Odd(-1)) // false
}

Ответ

Оператор остатка может дать отрицательный ответ, если дивиденд отрицательный: если n нечетное отрицательное число, n % 2 равно -1.

Частное q = x / y и остаток r = x % y удовлетворяют соотношениям

x = q*y + r  и  |r| < |y|

где х / у усекается до нуля.

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

Существует одно исключение: если x является наиболее отрицательным значением своего типа, частное q = x / -1 равно x.

Одно из решений - написать такую ​​функцию:

// Odd указывает, является ли n нечетным числом.
func Odd(n int) bool {
    return n%2 != 0
}

Вы также можете использовать побитовый оператор И &.

// Odd указывает, является ли n нечетным числом.
func Odd(n int) bool {
    return n&1 == 1
}


Читайте также:


суббота, 23 мая 2020 г.

Числа, начинающиеся с нуля в Golang

Что случилось со счетом в этом примере?

const (
    Century = 100
    Decade  = 010
    Year    = 001
)
// Самая старая женщина в мире, Эмма Морано,
// прожила век, две декады и два года.
fmt.Println("Ей было", Century+2*Decade+2*Year, "лет.")

Ей было 118 лет.

Ответ

010 - это число по основанию 8, поэтому оно означает 8, а не 10.

Целочисленные литералы в Go указываются в восьмеричном, десятичном или шестнадцатеричном формате. Число 16 может быть записано как 020, 16 или 0x10.

Литерал Основание Запись
020 8 Начинается с 0
16 10 Никогда не начинается с 0
0x10 16 Начинается с 0x

Существует множество способов записать ноль по основанию 8 в Go, в том числе 0, 00 и 000. Если вы предпочитаете шестнадцатеричную нотацию, у вас также есть набор параметров: 0x0, 0x00 и 0x000 (а также 0X0, 0X00 и 0X000). Однако в Go нет десятичного целочисленного литерала.

На самом деле, Go также не имеет отрицательных десятичных литералов: -1 - это унарный оператор отрицания, за которым следует десятичный литерал 1.


Читайте также:


пятница, 22 мая 2020 г.

Тройки Пифагора, побитовые операции и Golang

Тройки Пифагора являются целочисленными решениями теоремы Пифагора, a2 + b2 = c2.

Хорошо известным примером является (3, 4, 5):

fmt.Println(3^2+4^2 == 5^2) // true

Тройка (6, 8, 10) - еще один пример, но Go, похоже, не согласен.

fmt.Println(6^2+8^2 == 10^2) // false

Ответ

Обводное ^ обозначает побитовый XOR в Go. Вычисление, написанное в базе 2, выглядит следующим образом:

0011 ^ 0010 == 0001   (3^2 == 1)
0100 ^ 0010 == 0110   (4^2 == 6)
0101 ^ 0010 == 0111   (5^2 == 7)

Конечно, 1 + 6 == 7; Go и Пифагор с этим согласен.

Чтобы повысить целое число до степени 2, используйте умножение.

fmt.Println(6*6 + 8*8 == 10*10) // true

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


Читайте также:


Бесконечный цикл в Golang

Почему этот цикл работает вечно?

var b byte
for b = 250; b <= 255; b++ {
    fmt.Printf("%d %c\n", b, b)
}

Ответ

После итерации b == 255 выполняется b++. Это переполненяет byte (поскольку максимальное значение для byte составляет 255) и приводит к b == 0. Следовательно, b <= 255 все еще выполняется, и цикл перезапускается с 0.

Для целочисленных значений без знака (unsigned integer) операции +, -, * и << вычисляются по модулю 2n, где n - ширина бит целого типа без знака.

Для целых чисел со знаком (signed integer) операции +, -, * и << могут легально переполняться, и результирующее значение существует и детерминировано определяется представлением целого числа со знаком, операцией и ее операндами. Исключение не возникает в результате переполнения.

Спецификация языка программирования Go: арифметические операторы

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

var b byte
for b = 250; b < 256; b++ {
    fmt.Printf("%d %c\n", b, b)
}

../main.go:2:17: constant 256 overflows byte

Одним из решений является использование более широкого типа данных, такого как int.

for i := 250; i < 256; i++ {
    fmt.Printf("%d %c\n", i, i)
}

250 ú
251 û
252 ü
253 ý
254 þ
255 ÿ

Другой вариант - поставить тест завершения в конце цикла.

for b := byte(250); ; b++ {
    fmt.Printf("%d %c\n", b, b)
    if b == 255 {
        break
    }
}

250 ú
251 û
252 ü
253 ý
254 þ
255 ÿ


Читайте также:


четверг, 21 мая 2020 г.

Неожиданный ++, ожидая выражения

Почему эти строки не компилируются?

i := 0
fmt.Println(++i)
fmt.Println(i++)

main.go:9:14: syntax error: unexpected ++, expecting expression
main.go:10:15: syntax error: unexpected ++, expecting comma or )

Ответ

В Go операции инкремента и декремента не могут использоваться как выражения (expressions), только как утверждения (statements). Кроме того, допускается только постфиксная запись.

Вышеупомянутый фрагмент должен быть записан как:

i := 0
i++
fmt.Println(i)
fmt.Println(i)
i++

Без арифметики с указателями удобство значение операторов приращения префикса и постфикса уменьшается. В результате полного удаления их из иерархии выражений упрощается синтаксис выражений и устраняются беспорядочные вопросы, связанные с порядком вычисления ++ и - (например f(i++) и p[i] = q[++i]). Упрощение является значительным.


Читайте также:


Правильно расставьте приоритеты

Почему этот код не вычисляет количество часов и секунд?

n := 43210 // время в секундах
fmt.Println(n/60*60, "часов и", n%60*60, "секунд")

43200 часов и 600 секунд

Ответ

Операторы *, / и % имеют одинаковый приоритет и оцениваются слева направо: n/60*60 совпадает с (n/60)*60.

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

fmt.Println(n/(60*60), "часов и", n%(60*60), "секунд")

12 часов и 10 секунд

Или еще лучше, используйте константу.

const SecPerHour = 60 * 60
fmt.Println(n/SecPerHour, "часов и", n%SecPerHour, "секунд")

12 часов и 10 секунд


Читайте также:


среда, 20 мая 2020 г.

Константа переполняет int

Почему этот код не компилируется?

const n = 9876543210 * 9876543210
fmt.Println(n)

../main.go:2:13: constant 97546105778997104100 overflows int

Ответ

Нетипизированная константа n должна быть преобразована в тип, прежде чем ее можно будет присвоить параметру interface{} в вызове fmt.Println.

fmt.Println(a ...interface{})

Когда тип не может быть выведен из контекста, нетипизированная константа преобразуется в bool, int, float64, complex128, string или rune в зависимости от формата константы.

В этом случае константа является целым числом, но n больше максимального значения типа int.

Тем не менее, n может быть представлен как float64.

const n = 9876543210 * 9876543210
fmt.Println(float64(n))

9.75461057789971e+19

Для точного представления больших чисел пакет math/big реализует арифметику произвольной точности (длинная арифметика). Он поддерживает целые числа со знаком, рациональные числа и числа с плавающей точкой.


Читайте также:


Почему copy не копирует?

Почему копия исчезает?

var src, dst []int
src = []int{1, 2, 3}
copy(dst, src) // Копируем элементы в dst из src.
fmt.Println("dst:", dst)

dst: []

Ответ

Количество элементов, копируемых функцией copy, является минимумом len(dst) и len(src). Чтобы сделать полную копию, вы должны выделить достаточно большой целевой срез.

var src, dst []int
src = []int{1, 2, 3}
dst = make([]int, len(src))
n := copy(dst, src)
fmt.Println("dst:", dst, "(copied", n, "numbers)")

dst: [1 2 3] (copied 3 numbers)

Возвращаемое значение функции копирования - количество скопированных элементов.

Используя append

Вы также можете использовать функцию append, чтобы сделать копию, добавив к нулевому срезу.

var src, dst []int
src = []int{1, 2, 3}
dst = append(dst, src...)
fmt.Println("dst:", dst)

dst: [1 2 3]

Обратите внимание, что емкость среза, выделенного append, может быть немного больше, чем len(src).


Читайте также:


Необычный Trim в Golang

Что случилось со strings.TrimRight?

fmt.Println(strings.TrimRight("ABBA", "BA")) // Вывод: ""

Ответ

Функции Trim, TrimLeft и TrimRight удаляют все кодовые точки Unicode, содержащиеся в наборе. В этом случае все конечные A и B удаляются из строки, оставляя пустую строку.

Чтобы удалить заднюю строку, используйте strings.TrimSuffix.

fmt.Println(strings.TrimSuffix("ABBA", "BA")) // Вывод: "AB"


Читайте также:


купить игрушку gopher

вторник, 19 мая 2020 г.

Как складываются символы в Golang

Почему эти вызовы печати не дают тот же результат?

fmt.Println("H" + "i")
fmt.Println('H' + 'i')

Hi
177

Ответ

Литералы рун 'H' и 'i' являются целочисленными значениями, определяющими кодовые точки Unicode: 'H' - 72, 'i' - 105.

Вы можете превратить кодовую точку в строку с преобразованием.

fmt.Println(string(72) + string('i')) // "Hi"

Вы также можете использовать функцию fmt.Sprintf.

s := fmt.Sprintf("%c%c, world!", 72, 'i')
fmt.Println(s)// "Hi, world!"

Этот пост содержит список наиболее распространенных глаголов и флагов форматирования.


Читайте также:


Неизменяемые строки в Golang

Почему этот код не компилируется?

s := "hello"
s[0] = 'H'
fmt.Println(s)

../main.go:3:7: cannot assign to s[0]

Ответ

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

Чтобы обновить данные, используйте взамен срез рун.

buf := []rune("hello")
buf[0] = 'H'
s := string(buf)
fmt.Println(s)  // "Hello"

Если строка содержит только символы ASCII, вы также можете использовать байтовый срез, поскольку каждый ASCII символ занимает только 1 байт.


Читайте также:


понедельник, 18 мая 2020 г.

Неожиданный перевод строки

Почему эта программа не компилируется?

func main() {
    fruit := []string{
        "apple",
        "banana",
        "cherry"
    }
    fmt.Println(fruit)
}

../main.go:5:11: syntax error: unexpected newline, expecting comma or }

Ответ

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

func main() {
    fruit := []string{
        "apple",
        "banana",
        "cherry", // добавлена запятая
    }
    fmt.Println(fruit) // "[apple banana cherry]"
}

Такое поведение является следствием правил вставки точек с запятой в Go.

В результате вы можете добавлять и удалять строки без изменения окружающего кода.


Читайте также:


воскресенье, 17 мая 2020 г.

Затененные (shadowed) переменные в Golang

Почему переменная n не меняется?

func main() {
    n := 0
    if true {
        n := 1
        n++
    }
    fmt.Println(n) // 0
}

Ответ

Оператор

n := 1
объявляет новую переменную, которая затеняет оригинал n во всей области действия оператора if.

Чтобы повторно использовать n из внешнего блока, вместо этого напишите

n = 1

func main() {
    n := 0
    if true {
        n = 1
        n++
    }
    fmt.Println(n) // 2
}

Обнаружение затененных переменных

Чтобы помочь обнаружить затененные переменные, вы можете использовать экспериментальную функцию -shadow, предоставляемую инструментом vet. Он помечает переменные, которые могли быть непреднамеренно скрыты. Передача оригинальной версии кода vet выдает предупреждение.

$ go vet -shadow main.go
main.go:4: declaration of "n" shadows declaration at main.go:2

С версии Go 1.12 это больше не поддерживается. Вместо этого вы можете сделать

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

Кроме того, компилятор Go обнаруживает и запрещает некоторые случаи затенения.

func Foo() (n int, err error) {
    if true {
        err := fmt.Errorf("Invalid")
        return
    }
    return
}

../main.go:4:3: err is shadowed during return


Читайте также:


Массив не изменится

Почему значение массива сохраняется прежним?

func Foo(a [2]int) {
    a[0] = 8
}

func main() {
    a := [2]int{1, 2}
    Foo(a)         // Попытка изменить a[0].
    fmt.Println(a) // Вывод: [1 2]
}

Ответ

  • Массивы в Go являются значениями.
  • Когда вы передаете массив функции, массив копируется.

Если вы хотите, чтобы Foo обновлял элементы a, используйте вместо этого срез.

func Foo(a []int) {
    if len(a) > 0 {
        a[0] = 8
    }
}

func main() {
    a := []int{1, 2}
    Foo(a)         // Изменяем a[0].
    fmt.Println(a) // Вывод: [8 2]
}

Срез не хранит никаких данных, он просто описывает часть подлежащего массива.

Когда вы изменяете элемент среза, вы модифицируете соответствующий элемент его подлежащего массива, и другие срезы, которые совместно используют тот же подлежащий массив, увидят это изменение.


Читайте также:


Множественное значение в контексте одного значения в Golang

Почему этот код дает ошибку компиляции?

t := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z")
fmt.Println(t)

../main.go:9:17: multiple-value time.Parse() in single-value context

Ответ

Функция time.Parse возвращает два значения, time.Time и error, и вы должны использовать оба.

t, err := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z")
if err != nil {
    // TODO: Обработать ошибку.
}
fmt.Println(t)

2018-04-06 10:49:05 +0000 UTC

Пустой идентификатор

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

m := map[string]float64{"pi": 3.1416}
_, exists := m["pi"] // exists == true


Читайте также:


суббота, 16 мая 2020 г.

Неверный адрес памяти или разыменование нулевого указателя в Golang

Почему эта программа паникует?

type Point struct {
    X, Y float64
}

func (p *Point) Abs() float64 {
    return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

func main() {
    var p *Point
    fmt.Println(p.Abs())
}

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xd2c5a]

goroutine 1 [running]:
main.(*Point).Abs(...)
 ../main.go:6
main.main()
 ../main.go:11 +0x1a

Ответ

Неинициализированный указатель p в main функции равен nil, и вы не можете следовать за нулевым указателем.

Если x равен nil, попытка выполнить *x вызовет панику во время выполнения. Спецификация языка программирования Go: адресные операторы.

Вам необходимо создать Point. С помощью new:

func main() {
    var p *Point = new(Point)
    fmt.Println(p.Abs())
}

Либо с помощи литерала с оператором ссылки:

func main() {
    var p *Point = &Point{}
    fmt.Println(p.Abs())
}

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

func main() {
    var p Point // имеет нулевое значение Point{X:0, Y:0}
    fmt.Println(p.Abs())
}


Читайте также:


Присвоение записи в nil карте в Golang

Почему эта программа паникует?

var m map[string]float64
m["pi"] = 3.1416

panic: assignment to entry in nil map

Ответ

Вы должны инициализировать карту, используя функцию make (или литерал карты), прежде чем добавлять какие-либо элементы. Использование make:

m := make(map[string]float64)
m["pi"] = 3.1416

Использование литерала карты:

m := map[string]float64{}
m["pi"] = 3.1416


Читайте также:


пятница, 15 мая 2020 г.

Как использовать интерфейс io.Writer в Golang

Интерфейс io.Writer представляет объект, в который вы можете записать поток байтов.

type Writer interface {
    Write(p []byte) (n int, err error)
}

Write записывает до len(p) байтов из p в подлежащий поток данных - он возвращает количество записанных байтов и обнаруженную ошибку, которая привела к преждевременной остановке записи.

Стандартная библиотека предоставляет множество реализаций Writer, и Writers принимаются в качестве входных данных многими утилитами.

Как использовать встроенный writer

В качестве первого примера вы можете записать непосредственно в bytes.Buffer, используя функцию fmt.Fprintf. Это работает поскольку

  • bytes.Buffer имеет метод Write и
  • fmt.Fprintf принимает Writer в качестве первого аргумента.

var buf bytes.Buffer
fmt.Fprintf(&buf, "Size: %d MB.", 85)
s := buf.String()) // s == "Size: 85 MB."

Точно так же вы можете записывать напрямую в файлы или другие потоки, такие как http-соединения.

Это очень распространенный паттерн в Go. В качестве еще одного примера вы можете вычислить значение хэш-функции файла, скопировав файл в функцию io.Writer подходящего объекта hash.Hash.

Оптимизировать запись строк

Некоторые Writers в стандартной библиотеке имеют дополнительный метод WriteString. Этот метод может быть более эффективным, чем стандартный метод Write, так как он записывает строку напрямую, без выделения среза байтов.

Вы можете напрямую воспользоваться этой оптимизацией, используя функцию io.WriteString().

func WriteString(w Writer, s string) (n int, err error)

Если w реализует метод WriteString, он вызывается напрямую. В противном случае w.Write вызывается ровно один раз.


Читайте также:


Как использовать JSON в Golang

Формат обмена данными JSON удобен для чтения и записи человеком и эффективен для анализа и генерации машинами.

Типы по умолчанию

Типы Go по умолчанию для декодирования и кодирования JSON:

  • bool для логических выражений JSON
  • float64 для JSON чисел
  • string JSON для строк
  • nil для JSON null

Кроме того, time.Time и числовые типы в пакете math/big могут автоматически кодироваться как строки JSON.

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

Кодировать (marshal) структуру в JSON

Функция json.Marshal в пакете encoding/json генерирует данные JSON.

type FruitBasket struct {
    Name    string
    Fruit   []string
    Id      int64  `json:"ref"`
    private string // Неэкспортируемое поле не кодируется.
    Created time.Time
}

basket := FruitBasket{
    Name:    "Standard",
    Fruit:   []string{"Apple", "Banana", "Orange"},
    Id:      999,
    private: "Second-rate",
    Created: time.Now(),
}

var jsonData []byte
jsonData, err := json.Marshal(basket)
if err != nil {
    log.Println(err)
}
fmt.Println(string(jsonData))

Вывод:

{"Name":"Standard","Fruit":["Apple","Banana","Orange"],"ref":999,"Created":"2018-04-09T23:00:00Z"}

Только данные, которые могут быть представлены как JSON, будут закодированы.

  • Только экспортируемые (публичные) поля структуры будут присутствовать в выводе JSON. Другие поля игнорируются.
  • Поле с тегом json: сохраняется с именем тега вместо имени поля структуры.
  • Указатели будут закодированы как значения, на которые они указывают, или равны null, если указатель равен nil.

Отформатированная печать

Замените json.Marshal на json.MarshalIndent в примере выше, чтобы сделать отступ для вывода JSON.

jsonData, err := json.MarshalIndent(basket, "", "    ")

Вывод:

{
    "Name": "Standard",
    "Fruit": [
        "Apple",
        "Banana",
        "Orange"
    ],
    "ref": 999,
    "Created": "2018-04-09T23:00:00Z"
}

Декодировать (unmarshal) JSON в структуру

Функция json.Unmarshal в пакете encoding/json декодирует данные JSON.

type FruitBasket struct {
    Name    string
    Fruit   []string
    Id      int64 `json:"ref"`
    Created time.Time
}

jsonData := []byte(`
{
    "Name": "Standard",
    "Fruit": [
        "Apple",
        "Banana",
        "Orange"
    ],
    "ref": 999,
    "Created": "2018-04-09T23:00:00Z"
}`)

var basket FruitBasket
err := json.Unmarshal(jsonData, &basket)
if err != nil {
    log.Println(err)
}
fmt.Println(basket.Name, basket.Fruit, basket.Id)
fmt.Println(basket.Created)

Вывод:

Standard [Apple Banana Orange] 999
2018-04-09 23:00:00 +0000 UTC

Обратите внимание, что Unmarshal выделил новый срез самостоятельно. Вот как работает демаршалинг для срезов, карт и указателей.

Для заданного JSON ключа Foo Unmarshal попытается сопоставить поля структуры в следующем порядке:

  • экспортированное (публичное) поле с тегом json: "Foo"
  • экспортированное поле с именем Foo
  • экспортированное поле с именем FOO, FoO или другим сопоставлением без учета регистра

Только поля, которые найдены в типе назначения, будут декодированы:

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

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

Пакет encoding/json использует

  • map[string]interface{} для хранения произвольных объектов JSON
  • []interface{} для хранения произвольных массивов JSON

Любые допустимые данные JSON будут декодированы в обычном interface{} значении.

Рассмотрим данные JSON:

{
    "Name": "Eve",
    "Age": 6,
    "Parents": [
        "Alice",
        "Bob"
    ]
}

Функция json.Unmarshal будет декодировать их в карту, ключи которой являются строками, а сами значения хранятся как пустые значения интерфейса:

map[string]interface{}{
    "Name": "Eve",
    "Age":  6.0,
    "Parents": []interface{}{
        "Alice",
        "Bob",
    },
}

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

jsonData := []byte(`{"Name":"Eve","Age":6,"Parents":["Alice","Bob"]}`)

var v interface{}
json.Unmarshal(jsonData, &v)
data := v.(map[string]interface{})

for k, v := range data {
    switch v := v.(type) {
    case string:
        fmt.Println(k, v, "(string)")
    case float64:
        fmt.Println(k, v, "(float64)")
    case []interface{}:
        fmt.Println(k, "(array):")
        for i, u := range v {
            fmt.Println("    ", i, u)
        }
    default:
        fmt.Println(k, v, "(unknown)")
    }
}

Вывод:

Name Eve (string)
Age 6 (float64)
Parents (array):
     0 Alice
     1 Bob

Пример JSON файла

Типы json.Decoder и json.Encoder в пакете encoding/json обеспечивают поддержку чтения и записи потоков данных JSON, например, файлов.

Код в этом примере

  • читает поток объектов JSON из Reader (strings.Reader),
  • удаляет поле Age из каждого объекта,
  • и затем записывает объекты в Writer (os.Stdout).

const jsonData = `
    {"Name": "Alice", "Age": 25}
    {"Name": "Bob", "Age": 22}
`
reader := strings.NewReader(jsonData)
writer := os.Stdout

dec := json.NewDecoder(reader)
enc := json.NewEncoder(writer)

for {
    // Чтение одного объекта JSON и сохранение его в карте.
    var m map[string]interface{}
    if err := dec.Decode(&m); err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }

    // Удаление все пар ключ-значение 
    // с ключом == "Age" из карты.
    for k := range m {
        if k == "Age" {
            delete(m, k)
        }
    }

    //Запись карты как объект JSON.
    if err := enc.Encode(&m); err != nil {
        log.Println(err)
    }
}

Вывод:

{"Name":"Alice"}
{"Name":"Bob"}


Читайте также:


Как использовать интерфейс io.Reader в Golang

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

type Reader interface {
    Read(buf []byte) (n int, err error)
}

Read читает до len(buf) байтов в buf и возвращает количество прочитанных байтов - он возвращает ошибку io.EOF, когда заканчивается поток.

Стандартная библиотека предоставляет множество реализаций Reader (включая байтовые буферы в памяти, файлы и сетевые подключения), и Readers принимаются в качестве входных данных многими утилитами (включая реализации HTTP клиента и сервера).

Используйте встроенный ридер

Например, вы можете создать Reader из строки, используя функцию strings.Reader, а затем передать Reader непосредственно в функцию http.Post в пакете net/http. Затем Reader используется в качестве источника данных для POST запроса.

r := strings.NewReader("my request")
resp, err := http.Post("http://foo.bar",
 "application/x-www-form-urlencoded", r)

Поскольку http.Post использует Reader вместо []byte, тривиально, например, использовать вместо него содержимое файла.

Читайте прямо из потока байтов

Вы можете использовать функцию Read напрямую (это наименее распространенный вариант использования).

r := strings.NewReader("abcde")

buf := make([]byte, 4)
for {
    n, err := r.Read(buf)
    fmt.Println(n, err, buf[:n])
    if err == io.EOF {
        break
    }
}

4 <nil> [97 98 99 100]
1 <nil> [101]
0 EOF []

Используйте io.ReadFull для считывания ровно len(buf) байтов в buf:

r := strings.NewReader("abcde")

buf := make([]byte, 4)
if _, err := io.ReadFull(r, buf); err != nil {
    log.Fatal(err)
}
fmt.Println(buf)

if _, err := io.ReadFull(r, buf); err != nil {
    fmt.Println(err)
}

[97 98 99 100]
unexpected EOF

Используйте ioutil.ReadAll, чтобы прочитать все:

r := strings.NewReader("abcde")

buf, err := ioutil.ReadAll(r)
if err != nil {
    log.Fatal(err)
}
fmt.Println(buf)

[97 98 99 100 101]

Буферизированное чтение и сканирование

Типы bufio.Reader и bufio.Scanner обертывают Reader, создавая другой Reader, который также реализует интерфейс, но обеспечивает буферизацию и некоторую помощь для ввода текста.

В этом примере мы используем bufio.Scanner для подсчета количества слов в тексте.

const input = `Beware of bugs in the above code;
I have only proved it correct, not tried it.`

scanner := bufio.NewScanner(strings.NewReader(input))
// Настройка функции разделения.
scanner.Split(bufio.ScanWords) 

count := 0
for scanner.Scan() {
    count++
}
if err := scanner.Err(); err != nil {
    fmt.Println(err)
}
fmt.Println(count)

16


Читайте также:


четверг, 14 мая 2020 г.

Как реализовать итератор в Golang

Go имеет встроенный цикл range для перебора срезов, массивов, строк, карт и каналов.

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

Базовый шаблон итератора

// Iterate вызывает функцию f с n = 1, 2 и 3.
func Iterate(f func(n int)) {
    for i := 1; i <= 3; i++ {
        f(i)
    }
}

В использовании:

Iterate(func(n int) { fmt.Println(n) })

1
2
3

Итератор с break

// Iterate вызывает функцию f с n = 1, 2 и 3.
// Если f возвращает true, Iterate возвращается немедленно,
// пропуская все оставшиеся значения.
func Iterate(f func(n int) (skip bool)) {
    for i := 1; i <= 3; i++ {
        if f(i) {
            return
        }
    }
}

В использовании:

Iterate(func(n int) (skip bool) {
 fmt.Println(n)
 return n == 2
})

1
2


Читайте также:


среда, 13 мая 2020 г.

Анонимные функции и замыкания в Golang

Литерал функции (или лямбда) - это функция без имени.

В этом примере литерал функции передается в качестве аргумента less функции sort.Slice.

func Slice(slice interface{}, less func(i, j int) bool)

people := []string{"Alice", "Bob", "Dave"}
sort.Slice(people, func(i, j int) bool {
    return len(people[i]) < len(people[j])
})
fmt.Println(people)
// Вывод: [Bob Dave Alice]

Вы также можете использовать промежуточную переменную.

people := []string{"Alice", "Bob", "Dave"}
less := func(i, j int) bool {
    return len(people[i]) < len(people[j])
}
sort.Slice(people, less)

Обратите внимание, что функция less является замыканием: она ссылается на переменную people, которая объявлена ​​вне функции.

Замыкания

Литералы функций в Go являются замыканиями: они могут ссылаться на переменные, определенные в окружающей функции. Такие переменные

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

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

// New возвращает функцию Count.
// Count печатает количество раз, которое она был вызван.
func New() (Count func()) {
    n := 0
    return func() {
        n++
        fmt.Println(n)
    }
}

func main() {
    f1, f2 := New(), New()
    f1() // 1
    f2() // 1 (другая n)
    f1() // 2
    f2() // 2
}


Читайте также:


Типы функций и значения функций в Golang

Типы функций и значения функций могут использоваться и передаваться, как и другие значения:

type Operator func(x float64) float64

// Map применяет op к каждому элементу a.
func Map(op Operator, a []float64) []float64 {
    res := make([]float64, len(a))
    for i, x := range a {
        res[i] = op(x)
    }
    return res
}

func main() {
    op := math.Abs
    a := []float64{1, -2}
    b := Map(op, a)
    fmt.Println(b) // [1 2]

    c := Map(func(x float64) float64 { return 10 * x }, b)
    fmt.Println(c) // [10, 20]
}

Второй вызов Map использует литерал функции (или лямбду).

Тип функции описывает набор всех функций с одинаковыми параметрами и типами результатов.

  • Значение неинициализированной переменной типа функции равно nil.
  • Имена параметров являются необязательными.

Следующие два типа функций идентичны.

func(x, y int) int
func(int, int) int


Читайте также: