2017-08-04 71 views
-2

我想在一些Go代碼中初始化一個程序包範圍的變量來連接數據庫,但我總是得到一個零指針異常,所以很明顯,這個賦值沒有發生正常。這將引發一個錯誤:Golang的init()函數中的程序包範圍變量賦值

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似乎暗示使用包級數據庫連接比根據需要創建/關閉連接更好。

回答

2

我應該從哪裏開始?讓我們從您的第一個代碼片段開始,並使其正常工作。

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範圍。

我希望它有幫助。

+0

感謝您的回覆,這確實有幫助。我誤解了':='操作符是要聲明「新」變量,而不是處理範圍界定。 Go對我來說還很新,所以非常感謝您花時間回覆。 – carusot42

+0

@ carusot42不客氣。快樂學習。 – jeevatkm