Как использовать глобальный var для файлов в пакете?
У меня есть следующая структура файла:
модели /db.go
type DB struct {
*sql.DB
}
var db *DB
func init() {
dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable",
DB_USER, DB_PASSWORD, DB_NAME)
db, err := NewDB(dbinfo)
checkErr(err)
rows, err := db.Query("SELECT * FROM profile")
checkErr(err)
fmt.Println(rows)
}
func NewDB(dataSourceName string) (*DB, error) {
db, err := sql.Open("postgres", dataSourceName)
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
return nil, err
}
return &DB{db}, nil
}
модели /db _util.go
func (p *Profile) InsertProfile() {
if db != nil {
_, err := db.Exec(...)
checkErr(err)
} else {
fmt.Println("DB object is NULL")
}
}
Когда я пытаюсь получить доступ к db
в InsertProfile
, он говорит NULL ptr exception
. Как получить доступ к db
в db_utils.go
?
Я бы не хотел использовать db
(так как он предоставил бы доступ ко всем пакетам).
Я возвращаю QUERY из db
в init()
правильно.
Ответы
Ответ 1
Изменить: Проблема заключается в том, что вы использовали краткое объявление переменной :=
, и вы только что сохранили созданный *DB
в локальной переменной, а не в глобальной.
Эта строка:
db, err := NewDB(dbinfo)
Создает 2 локальные переменные: db
и err
, и этот локальный db
не имеет ничего общего с вашей глобальной переменной db
. Ваша глобальная переменная останется nil
. Вы должны назначить созданную *DB
глобальной переменной. Не используйте короткое объявление переменной, но просто assignment, например:
var err error
db, err = NewDB(dbinfo)
if err != nil {
log.Fatal(err)
}
Далее следует исходный ответ.
Это тип указателя, вы должны инициализировать его, прежде чем использовать его. Нулевое значение для типов указателей nil
.
Вам не нужно экспортировать его (что начинается с заглавной буквы). Обратите внимание, что не имеет значения, что у вас есть несколько файлов, если они являются частью одного и того же пакета, они могут получить доступ к идентификаторам, определенным друг другом.
Хорошим решением было бы сделать это в функции пакета init()
, которая вызывается автоматически.
Обратите внимание, что sql.Open()
может просто подтвердить свои аргументы, не создавая соединение с базой данных. Чтобы проверить правильность имени источника данных, вызовите DB.Ping()
.
Например:
var db *sql.DB
func init() {
var err error
db, err = sql.Open("yourdrivername", "somesource")
if err != nil {
log.Fatal(err)
}
if err = db.Ping(); err != nil {
log.Fatal(err)
}
}
Ответ 2
icza уже правильно ответила на вашу конкретную проблему, но стоит добавить дополнительное объяснение того, что вы делаете неправильно, поэтому понимаете, как не совершить ошибку в будущем. В Go синтаксис :=
для присваивания создает новые переменные с именами слева от :=
, возможно, затеняющего пакета или даже родительских переменных/переменных объекта. В качестве примера:
package main
import "fmt"
var foo string = "global"
func main() {
fmt.Println(foo) // prints "global"
// using := creates a new function scope variable
// named foo that shadows the package scope foo
foo := "function scope"
fmt.Println(foo) // prints "function scope"
printGlobalFoo() // prints "global"
if true {
foo := "nested scope"
fmt.Println(foo) // prints "nested scope"
printGlobalFoo() // prints "global"
}
// the foo created inside the if goes out of scope when
// the code block is exited
fmt.Println(foo) // prints "function scope"
printGlobalFoo() // prints "global"
if true {
foo = "nested scope" // note just = not :=
}
fmt.Println(foo) // prints "nested scope"
printGlobalFoo() // prints "global"
setGlobalFoo()
printGlobalFoo() // prints "new value"
}
func printGlobalFoo() {
fmt.Println(foo)
}
func setGlobalFoo() {
foo = "new value" // note just = not :=
}
Примечание. Go не имеет возможности удалить или отключить переменную, поэтому, если вы затеняете более широкие переменные области (например, создавая переменную области видимости функции с тем же именем, что и переменная области пакета), нет доступа к ней более высокая переменная области видимости внутри этого кодового блока.
Также имейте в виду, что :=
является сокращением для var foo =
. Оба действуют точно так же, однако :=
является допустимым синтаксисом внутри функции или метода, тогда как синтаксис var
действителен везде.