How to use global var across files in a package?

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

How to use global var across files in a package?

问题

我有以下的文件结构:

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

当我尝试在InsertProfile函数中访问db时,它显示NULL ptr exception。我该如何在db_util.go中访问db

我不想将db大写(因为这将给予所有包访问权限)。

我能够正确获取init()函数中db的查询结果。

英文:

I have the following file structure:

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

When I try to access db in InsertProfile function, it says NULL ptr exception. How do I access the db in db_utils.go?

I would not like to capitalize db (as it would give access to all the packages).

I am getting the QUERY returned from the db in init() correctly.

答案1

得分: 60

问题是你使用了短变量声明 :=,并且你只是将创建的*DB值存储在了一个局部变量中,而不是全局变量中。

这一行代码:

db, err := NewDB(dbinfo)

创建了两个局部变量:dberr,而这个局部的db与你的全局变量db没有任何关系。你的全局变量仍然是nil。你需要将创建的*DB赋值给全局变量。不要使用短变量声明,而是使用简单的赋值,例如:

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)
    }
}
英文:

Edit: The problem is that you used Short variable declaration := and you just stored the created *DB value in a local variable and not in the global one.

This line:

db, err := NewDB(dbinfo)

Creates 2 local variables: db and err, and this local db has nothing to do with your global db variable. Your global variable will remain nil. You have to assign the created *DB to the global variable. Do not use short variable declaration but simple assignment, e.g:

var err error
db, err = NewDB(dbinfo)
if err != nil {
    log.Fatal(err)
}

Original answer follows.


It's a pointer type, you have to initialize it before you use it. The zero value for pointer types is nil.

You don't have to export it (that's what starting it with a capital letter does). Note that it doesn't matter that you have multiple files as long as they are part of the same package, they can access identifiers defined in one another.

A good solution would be to do it in the package init() function which is called automatically.

Note that sql.Open() may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call DB.Ping().

For example:

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

答案2

得分: 17

icza已经正确回答了你的具体问题,但值得补充一些额外的解释,以便你理解你犯了什么错误,以免将来再犯同样的错误。在Go语言中,赋值语句:=会创建新的变量,变量名位于:=左侧,可能会遮蔽包甚至父级作用域的函数/方法变量。举个例子:

package main

import "fmt"

var foo string = "global"

func main() {
    fmt.Println(foo) // 输出 "global"
    
    // 使用 := 创建一个新的函数作用域变量
    // 名为 foo,遮蔽了包作用域的 foo
    foo := "function scope" 
    fmt.Println(foo) // 输出 "function scope"
    printGlobalFoo() // 输出 "global"
    
    if true {
        foo := "nested scope"
        fmt.Println(foo) // 输出 "nested scope"
        printGlobalFoo() // 输出 "global" 
    } 
    // if 代码块结束时,if 内部创建的 foo 就会超出作用域

    fmt.Println(foo) // 输出 "function scope"
    printGlobalFoo() // 输出 "global"

    if true {
        foo = "nested scope" // 注意这里只有 = 没有 :=
    }

    fmt.Println(foo) // 输出 "nested scope"
    printGlobalFoo() // 输出 "global"

    setGlobalFoo()
    printGlobalFoo() // 输出 "new value"
}

func printGlobalFoo() {
    fmt.Println(foo)
}

func setGlobalFoo() {
    foo = "new value" // 注意这里只有 = 没有 :=
}

需要注意的是,Go语言没有删除或取消变量的方法,因此一旦你遮蔽了更高作用域的变量(例如创建了与包作用域变量同名的函数作用域变量),就无法在该代码块内访问更高作用域的变量。

另外要注意,:=var foo =的简写形式。它们的行为完全相同,但是:=只在函数或方法内部是有效的语法,而var语法则在任何地方都有效。

英文:

icza has already correctly answered your specific problem but it's worth adding some additional explanation on what you're doing wrong so you understand how not to make the mistake in the future. In Go, the syntax := for assignment creates new variables with the names to the left of the :=, possibly shadowing package, or even parent scope function/method variables. As an example:

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 :=
}

Note Go has no way to delete or unset a variable, so once you have shadowed a higher scope variables (such as by creating a function scope variable of the same name as a package scope variable), there is no way to access the higher scope variable within that code block.

Also be aware that := is a shorthand for var foo =. Both act in exactly the same way, however := is only valid syntax within a function or method, while the var syntax is valid everywhere.

答案3

得分: 11

对于来到这里并希望快速得到答案的人。

db.go 文件中:

package db

var db *DB

type DB struct {
	*gorm.DB // 或者你想要的数据库,比如 *mongo.Client
}

func GetDB() *DB {
	if db == nil{
		db = ConnectToYourDbFunc("connection_string")
	}
	return db
}

然后在你的其他包中,你可以通过以下方式获取它:

db := db.GetDB()

就是这样。

英文:

For who came here and wants a fast answer.

in db.go file:

package db

var db *DB

type DB struct {
	*gorm.DB // or what database you want like *mongo.Client
}

func GetDB() *DB {
	if db == nil{
		db = ConnectToYourDbFunc("connection_string")
	}
	return db
}

then in your other packages you can get it just with this:

db := db.GetDB()

thats all.

huangapple
  • 本文由 发表于 2015年12月10日 14:43:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/34195360.html
匿名

发表评论

匿名网友

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

确定