英文:
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 isif else
scope.
I hope it helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论