web-dev-qa-db-ja.com

パッケージ内のファイル間でグローバル変数を使用する方法は?

私は次のファイル構造を持っています:

models/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
}
_

models/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_utils.go_のdbにアクセスするにはどうすればよいですか?

dbを大文字にしたくありません(すべてのパッケージにアクセスできるため)。

init()dbから返されるクエリを正しく取得しています。

29
lionelmessi

編集:問題は、 短い変数宣言 _:=_を使用し、作成した_*DB_グローバル変数ではなくローカル変数の値。

この行:

_db, err := NewDB(dbinfo)
_

dberrの2つのローカル変数を作成します。このローカル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)
    }
}
_
39
icza

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構文はどこでも有効です。

12
Endophage