Golang的init()函数中的包广泛变量赋值

时间:2017-08-04 03:57:01

标签: variables go global-variables

我想在一些Go代码中初始化一个包范围的变量来连接到数据库,但是我一直得到一个nil指针异常,所以很明显分配没有正确发生。这会引发错误:

package main

import (
"fmt"
"database/sql"

_ "github.com/lib/pq"
)

var connection *sql.DB

func init() {
    connectionString := "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable"

    connection, err := sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

func main() {
    TestConnect()
}

func TestConnect() {
    var pid int

    err := connection.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
    check(err)
    fmt.Printf("pid: %v", pid)
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

这是错误:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4c1a1a]

goroutine 1 [running]:
database/sql.(*DB).conn(0x0, 0x70b7c0, 0xc4200102b8, 0x1, 0xc420055e08, 0x4c28df, 0xc4200b0000)
    /usr/local/go/src/database/sql/sql.go:896 +0x3a
database/sql.(*DB).query(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x101, 0x741c80, ...)
    /usr/local/go/src/database/sql/sql.go:1245 +0x5b
database/sql.(*DB).QueryContext(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x0, 0x8, ...)
    /usr/local/go/src/database/sql/sql.go:1227 +0xb8
database/sql.(*DB).QueryRowContext(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0xc420010cb0)
    /usr/local/go/src/database/sql/sql.go:1317 +0x90
database/sql.(*DB).QueryRow(0x0, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x0)
    /usr/local/go/src/database/sql/sql.go:1325 +0x7c
main.TestConnect()
    /home/tom/go/src/go-rest/ignore/connect.go:32 +0x82
main.main()
    /home/tom/go/src/go-rest/ignore/connect.go:26 +0x20
exit status 2

但是,如果我将其切换为可以使用=上的connection运算符代替:= ...

package main

import (
    "fmt"
    "database/sql"

    _ "github.com/lib/pq"
)

var connection *sql.DB

func init() {
    connectionString, err := GetConnectionString()

    connection, err = sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

func main() {
    TestConnect()
}

func TestConnect() {
    var pid int

    err := connection.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
    check(err)
    fmt.Printf("pid: %v", pid)
}

func GetConnectionString() (string, error) {
    return "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable", nil
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

然后一切都按预期工作,并正确分配变量。因为先前未定义err变量,所以我必须使用:=进行赋值,但这似乎假设我正在初始化函数范围的connection变量,而不是尝试赋值到包范围的connection变量。有没有办法在Go中强制执行此任务?就目前而言,我需要编写无用的样板代码以使其以正确的方式工作,并且似乎应该有更好的方法。

当然,这种可能性也表明我正在尝试做一些我不应该做的事情。根据我的研究,this guide似乎建议使用包范围的数据库连接比根据需要创建/关闭连接更好。

1 个答案:

答案 0 :(得分:3)

我应该从哪里开始?让我们从您的第一个代码段开始,让它正常工作。

func init() {
    connectionString := "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable"

    var err error
    connection, err = sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

在上面的代码段中,err是局部变量,connection是包变量。

在您的第二个代码段中,err被定义为部分connectionString, err := ...,并且您定义了connection个包变量。所以它有效。

使用短声明运算符:=定义本地范围的变量。如:

  • 如果您在func内定义,那么它就是功能范围。
  • 如果您在if中定义,那么它是if else范围。

我希望它有所帮助。