在Golang的init()函数中进行包范围的变量赋值。

huangapple go评论68阅读模式
英文:

Package-wide variable assignment in Golang's init() function

问题

我想在一些Go代码中初始化一个全局变量,用于连接数据库,但是我一直得到一个空指针异常,所以显然赋值没有正确进行。这会抛出一个错误:

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中有没有一种方法可以强制进行这种赋值?目前,我需要编写无用的样板代码才能使其正常工作,而且似乎应该有更好的方法。

当然,也有可能我正在尝试做一些我可能不应该做的事情。根据我的研究,这个指南似乎建议使用全局数据库连接比根据需要创建/关闭连接更好。

英文:

I want to initialize a package-wide variable in some Go code to connect to a database, but I keep getting a nil pointer exception, so clearly the assignment isn't occurring properly. This throws an error:

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)
	}
}

Here's the error:

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

However, if I switch it up so I can use the = operator on connection instead of :=...

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)
	}
}

Then everything works as expected, and the variable is properly assigned. Because the err variable is previously undefined, I have to use := for assignment, but this seems to assume that I'm initializing a function-scoped connection variable, and not trying to assign to the package-scoped connection variable. Is there a way to force this assignment in Go? As it stands, I need to write useless, boilerplate code to get this to work the right way, and it seems like there should be a better way.

Of course, the possibility also stands that I'm trying to do something that I perhaps shouldn't be. Based on my research though, this guide seems to suggest using a package-wide database connection is better then creating / closing connections as needed.

答案1

得分: 7

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

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


在你的第二个代码片段中,err 是在 connectionString, err := ... 部分定义的,并且你定义了 connection 包变量。所以它可以工作。


使用短声明运算符 := 定义了局部作用域的变量。例如:

  • 如果你在 func 中定义,那么它是函数作用域。
  • 如果你在 if 中定义,那么它是 if else 作用域。

希望对你有帮助。

英文:

Where should I start? Let's start with your first code snippet and make it work properly.

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)
}

In the above code snippet err is local variable and connection is package variable.


In your second code snippet err is defined as part connectionString, err := ... and you have connection package variable defined. So it works.


Using short declaration operator := defines local scoped variable. Such as:

  • If you define within func then it is function scope.
  • If you define in if then it is if else scope.

I hope it helps.

huangapple
  • 本文由 发表于 2017年8月4日 11:57:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/45498036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定