Область видимости и пакеты
Область видимости (scope) определяет, где доступна переменная. В Go это интуитивно - но есть подводные камни.
Уровни видимости
Уровень пакета
package main
var globalVar = "я видна везде в пакете"
const maxRetries = 3
func main() {
fmt.Println(globalVar) // OK
fmt.Println(maxRetries) // OK
}
func otherFunc() {
fmt.Println(globalVar) // OK - тоже видна
}
Уровень функции
func calculate() {
result := 42 // видна только внутри calculate
fmt.Println(result) // OK
}
func main() {
calculate()
// fmt.Println(result) // ОШИБКА: result не определена
}
Уровень блока
func main() {
x := 10
if x > 5 {
y := 20 // видна только внутри if
fmt.Println(x, y) // OK: x=10, y=20
}
// fmt.Println(y) // ОШИБКА: y не определена
for i := 0; i < 3; i++ {
temp := i * 2 // видна только внутри for
fmt.Println(temp)
}
// fmt.Println(i) // ОШИБКА: i не определена
// fmt.Println(temp) // ОШИБКА: temp не определена
}
Shadowing - затенение переменных
Самый коварный подводный камень Go:
func main() {
x := 10
fmt.Println(x) // 10
if true {
x := 20 // НОВАЯ переменная x, затеняет внешнюю!
fmt.Println(x) // 20
}
fmt.Println(x) // 10 - внешняя не изменилась!
}
Как избежать shadowing
// ПЛОХО: shadowing err
var err error
if true {
result, err := doSomething() // новый err
_ = result
}
// err всё ещё nil
// ХОРОШО: присваивание в существующую
var err error
var result int
if true {
result, err = doSomething() // = вместо :=
}
Утилита для поиска shadowing:
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
shadow ./...
Экспорт: заглавная буква = публичный
В Go нет ключевых слов public/private. Регистр первой буквы решает всё:
package user
type User struct {
Name string // Экспортируемое (публичное)
Email string // Экспортируемое
age int // НЕ экспортируемое (приватное)
}
func NewUser(name string) *User { // Экспортируемая функция
return &User{
Name: name,
age: defaultAge(), // вызов приватной функции
}
}
func defaultAge() int { // НЕ экспортируемая
return 0
}
Из другого пакета:
package main
import "myapp/user"
func main() {
u := user.NewUser("Alice") // OK
fmt.Println(u.Name) // OK
// fmt.Println(u.age) // ОШИБКА: age не экспортировано
// user.defaultAge() // ОШИБКА: не экспортировано
}
<ComparisonTable data={{ headers: ["Имя", "Видимость", "Примеры"], rows: [ ["Name, DoSomething, MaxSize", "Публичное (экспортируемое)", "Доступно из других пакетов"], ["name, doSomething, maxSize", "Приватное", "Только внутри своего пакета"], ["_name, ", "Blank identifier", " = игнорировать значение"] ] }} />
Организация пакетов
myproject/
├── go.mod
├── main.go // package main
├── config/
│ └── config.go // package config
├── handler/
│ └── user.go // package handler
└── model/
└── user.go // package model
// model/user.go
package model
type User struct {
ID int
Name string
}
// main.go
package main
import "myproject/model"
func main() {
u := model.User{ID: 1, Name: "Alice"}
fmt.Println(u)
}
init() - автоматическая инициализация
package config
var Settings map[string]string
func init() {
// Вызывается автоматически при импорте пакета
Settings = make(map[string]string)
Settings["env"] = "development"
}
Порядок: переменные пакета → init() → main().