Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use global var across files in a package?

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.

like image 836
lionelmessi Avatar asked Dec 10 '15 06:12

lionelmessi


People also ask

Can global variables be used in different files?

A global variable is accessible to all functions in every source file where it is declared. To avoid problems: Initialization — if a global variable is declared in more than one source file in a library, it should be initialized in only one place or you will get a compiler error.

How can you share global variables across module?

To share global variables across modules within a single program, create a special module. Import the config module in all modules of your application. The module will be available as a global variable across modules.


2 Answers

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)     } } 
like image 150
icza Avatar answered Sep 19 '22 21:09

icza


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.

like image 27
Endophage Avatar answered Sep 22 '22 21:09

Endophage