Программирование на языке Go: полезные советы | OTUS

Курсы

Курсы в разработке Подготовительные курсы
Работа в компаниях Компаниям Блог +7 499 110-61-65

Программирование на языке Go: полезные советы

Go_deep_10.9-5020-12a219.png

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

1. Как заставить пользователей пакетов использовать имена полей в составных литералах для структур?

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

Пример:

// foo.go
package foo

type Config struct {
    _    [0]int
    Name string
    Size int
}
// main.go
package main

import "foo"

func main() {
    //_ = foo.Config{[0]int{}, "bar", 123} // error
    _ = foo.Config{Name: "bar", Size: 123} // compile ok
}

Не рекомендуется размещать неэкспортируемые поля нулевого размера в структуре последними, поскольку это может увеличить размер структуры.

2. Как создать структурный тип, который невозможно использовать в операциях сравнения?

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

Пример:

package main

type T struct {
    dummy        [0]func()
    AnotherField int
}

var x map[T]int // ошибка компилятора: тип Т не может использоваться в качестве ключа отображения (invalid map key type T)

func main() {
    var a, b T
    _ = a == b // ошибка компилятора: недопустимая операция (invalid operation)
}

3. Не используйте оператор присваивания с взаимозависимыми выражениями

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

В некоторых неудачных вариантах присваивания отдельных значений также возможен неопределенный порядок вычисления выражений. Например, следующая программа выводит [7 0 9], [0 8 9] или [7 8 9] в зависимости от реализации компилятора.

package main

import "fmt"

var a = &[]int{1, 2, 3}
var i int
func f() int {
    i = 1
    a = &[]int{7, 8, 9}
    return 0
}

func main() {
    // Порядок вычисления a, i
    // и f() не определен.
    (*a)[i] = f()
    fmt.Println(*a)
}

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

4. Варианты реализации оператора for i in 0..N, используемого в других языках

Для имитации такого цикла можно осуществить перебор массива с элементами нулевого размера или использовать пустой (nil) указатель на массив.

Пример:

package main

import "fmt"

func main() {
    const N = 5

    for i := range [N]struct{}{} {
        fmt.Println(i)
    }
    for i := range [N][0]int{} {
        fmt.Println(i)
    }
    for i := range (*[N]int)(nil) {
        fmt.Println(i)
    }
}

5. Значения некоторых типов стандартных пакетов не предназначены для копирования

В частности, не рекомендуется выполнять копирование значений типов bytes.Buffer и strings.Builder, а также типов пакета sync (в некоторых особых случаях безопасное копирование значений данных типов возможно, но лучше их не копировать).

Реализация типа strings.Builder отслеживает некорректные копии значений этого типа. При обнаружении такой копии в процессе исполнения программы возникает состояние «паники» (panic).

Пример:

package main

import "strings"

func main() {
    var b strings.Builder
    b.WriteString("hello ")
    var b2 = b
    b2.WriteString("world!") // паника
}

При копировании значений типов стандартного пакета sync команда go vet из комплекта Go SDK выдаст соответствующее предупреждение.

// demo.go
package demo

import "sync"

func f(m sync.Mutex) { // warning
    m.Lock()
    defer m.Unlock()
    // do something ...
}
$ go vet demo.go
./demo.go:5: f passes lock by value: sync.Mutex

Попытка копирования значений bytes.Buffer не будет обнаружена ни в процессе выполнения, ни командой go vet. Так что, будьте внимательны и не копируйте их.

Это первый блок наших советов по Go. Продолжение следует, следите за новостями!

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться