Программирование на языке Go: полезные советы. Часть 1
Предлагаем вашему вниманию подборку полезных советов, которые будут интересны разработчикам 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) существует неопределенность порядка вычисления значений, присваиваемых одним оператором нескольким переменным с использованием взаимозависимых выражений. Таким образом, следует разделить оператор множественного присваивания значений на несколько присваиваний отдельных значений, если между выражениями, значения которых присваиваются, есть зависимости либо если вы не уверены, что таких зависимостей нет.
В некоторых неудачных вариантах присваивания отдельных значений также возможен неопределенный порядок вычисления выражений. Например, следующая программа выводит
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. Продолжение следует, следите за новостями!