Хотя она не выглядит внешне очень отличающейся от инициализации в C или C++, инициализация в Go более мощная. Сложные структуры могут быть построены во время инициализации, а проблемы упорядочения среди инициализированных объектов, даже среди разных пакетов, обрабатываются правильно.
Константы
Константы в Go - это просто константы. Они создаются во время компиляции, даже если они определены как локальные в функциях, и могут быть только числами, символами (рунами), строками или булевыми типами. Из-за ограничения времени компиляции выражения, которые определяют их должны быть константными выражениями, оценивающимися компилятором. Например,
1 << 3
является константным выражением, а math.Sin(math.Pi/4)
нет, потому что вызов функции для math.Sin
случится во время выполнения.
В Go перечисляемые константы создаются с использованием iota
енумератора. Поскольку iota
может быть частью выражения и выражения могут быть неявно повторены, легко построить сложные
наборы значений.
type ByteSize float64
const (
// игнорируем первое значение,
// производя присваивание к пустому идентификатору
_ = iota
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
Возможность прикрепить метод, такой как String
, к любому определенному пользователем типу позволяет произвольным значениям форматировать себя автоматически для печати. Хотя вы увидите, что это чаще всего применяется к структурам, этот метод также полезен для скалярных типов, таких как типы с плавающей точкой, такие как ByteSize
.
func (b ByteSize) String() string {
switch {
case b >= YB:
return fmt.Sprintf("%.2fYB", b/YB)
case b >= ZB:
return fmt.Sprintf("%.2fZB", b/ZB)
case b >= EB:
return fmt.Sprintf("%.2fEB", b/EB)
case b >= PB:
return fmt.Sprintf("%.2fPB", b/PB)
case b >= TB:
return fmt.Sprintf("%.2fTB", b/TB)
case b >= GB:
return fmt.Sprintf("%.2fGB", b/GB)
case b >= MB:
return fmt.Sprintf("%.2fMB", b/MB)
case b >= KB:
return fmt.Sprintf("%.2fKB", b/KB)
}
return fmt.Sprintf("%.2fB", b)
}
Выражение YB
печатается как 1.00YB
, в то время как ByteSize(1e13)
печатается как 9.09TB
.
Использование здесь Sprintf
для реализации в ByteSize
метода String
безопасно (избегает бесконечного повторения) не из-за преобразования, а потому что он вызывает Sprintf
с %f
, который не является строковым форматом: Sprintf
будет вызывать только метод String
, когда ему нужна строка, и %f
ожидает значение с плавающей точкой.
Переменные
Переменные могут быть инициализированы так же, как и константы, но инициализатор может быть общим выражением, вычисляемым во время выполнения.
var (
home = os.Getenv("HOME")
user = os.Getenv("USER")
gopath = os.Getenv("GOPATH")
)
Функция init
Наконец, каждый исходный файл может определить свою собственную функцию init
для установки любого необходимого состояния. (На самом деле каждый файл может иметь несколько init
.) init
вызывается после того как все объявления переменных в пакете были оценены их инициализаторами, и они оцениваются только после того, как все импортированные пакеты были инициализированы.
Помимо инициализаций, которые не могут быть выражены как объявления, обычное использование функций init
- это проверка или исправление правильности состояния программы до начала реального выполнения.
func init() {
if user == "" {
log.Fatal("$USER not set")
}
if home == "" {
home = "/home/" + user
}
if gopath == "" {
gopath = home + "/go"
}
// gopath может быть переписан
// --gopath флагом в командной строке.
flag.StringVar(&gopath, "gopath", gopath,
"override default GOPATH")
}
Читайте также:
Комментариев нет:
Отправить комментарий