Подтвердить что ты не робот

Как использовать глобальный 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() правильно.

4b9b3361

Ответ 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 действителен везде.