Что означает символ подчеркивания перед импортом пакета в Go?
Что такое пустой импорт в Go?
Символ подчеркивания _ перед именем пакета в Go — это специальный синтаксис, который называется пустым импортом (blank import) или импортом ради побочного эффекта (import for side effect).
Как это работает?
В отличие от Python, в Go импорт пакета без его использования — это ошибка компиляции. Но есть ситуации, когда пакет нужно импортировать только ради его инициализации, не используя его функции или типы явно. Для этого и используется _.
Каждый пакет в Go может иметь функцию init(), которая выполняется автоматически при импорте пакета. Пустой импорт заставляет Go выполнить эту функцию, но при этом не требует явного использования пакета в коде.
Реальный пример: драйверы баз данных
Самый частый случай — это регистрация драйверов баз данных. Рассмотрим ваш пример с PostgreSQL:
package main
import (
"database/sql"
_ "github.com/lib/pq" // Регистрирует драйвер PostgreSQL
)
func main() {
db, err := sql.Open("postgres", "user=... dbname=...")
// ...
}
Пакет github.com/lib/pq в своей функции init() вызывает:
func init() {
sql.Register("postgres", &Driver{})
}
Это регистрирует драйвер в глобальном реестре пакета database/sql. После этого вы можете использовать строку "postgres" в sql.Open(). Без пустого импорта драйвер не будет зарегистрирован, и база данных не подключится.
Другие случаи использования
- Регистрация HTTP-хендлеров — пакеты вроде
net/http/pprofдобавляют свои обработчики в стандартный HTTP-сервер - Автоматическая инициализация — загрузка конфигураций, подключение к сервисам
- Тестирование — пакеты, которые должны инициализироваться для тестов, но не используются напрямую
Важные моменты
- Функция
init()выполняется один раз при первом импорте пакета - Порядок выполнения
init()гарантирован: сначала в импортированных пакетах, потом в текущем - Пустой импорт — это единственный легальный способ импортировать пакет без его использования
Проверка: что произойдет?
Если вы напишете:
import (
"fmt"
_ "fmt" // Ошибка! Пакет уже импортирован
)
Компилятор выдаст ошибку, потому что один пакет нельзя импортировать дважды (даже с пустым идентификатором).
Альтернатива: точка-импорт
Существует похожий синтаксис import . "package", который делает все экспортируемые имена пакета доступными без префикса. Но это совсем другая история и обычно не рекомендуется.
Дополнение: как найти, что делает пустой импорт
Если вы встретили пустой импорт и хотите понять, зачем он нужен, сделайте следующее:
- Найдите пакет в
go.modили в папкеvendor - Откройте файлы пакета и найдите функцию
init()(их может быть несколько) - Посмотрите, что регистрируется или инициализируется
Пример поиска
Для пакета github.com/lib/pq функция init() находится в файле conn.go:
func init() {
sql.Register("postgres", &Driver{})
sql.Register("pq", &Driver{})
}
Как видите, регистрируются два имени драйвера: "postgres" и "pq". Оба можно использовать в sql.Open().
Когда пустой импорт НЕ нужен
Если вы используете функции или типы из пакета напрямую, пустой импорт не нужен. Просто импортируйте пакет обычным способом:
import "fmt" // Нормальный импорт, используем fmt.Println()
import _ "fmt" // Ошибка компиляции: пакет импортирован, но не используется
Пустой импорт — это осознанное решение разработчика пакета, который хочет, чтобы его код выполнялся при импорте, но не хотел бы навязывать пользователям конкретные функции.