I'm facing a wierd problem with Golang.
On init() function, i want to assign a value to my variable that was declared outside this function.
But to assign the value to this var, i need to get
error
to check if everything is ok.
Here is the code:
var retryValue time.Duration
func init() {
retryValue, err := time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
func a(){
fmt.Println(retryValue)
}
And i get the compiling error:
retryValue declared and not used
I need to change init() to this:
func init() {
var err error
retryValue, err = time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
There is another way to solve this problem?
:=
always create a new variable if one of the variables are already declared? It's about variable golang's sope?
Thanks!
There are two basic ways to do this. You found one:
var retryValue time.Duration
func init() {
var err error
retryValue, err = time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
A slightly shorter method would be:
var retryValue = func() time.Duration {
rv, err := time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
return rv
}()
But better than either of these, would be to just specify the value directly, rather than parsing it:
var retryValue = 15 * time.Minute // or whatever value you want
Your fix is indeed the shortest way to do this, and no there is not another way to solve the issue.
This problem is rare enough that it is not really an issue in practice, after all it is only one more (short) line.
Related
I am calling this code:
package multicall
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
var multicallContractAddress = common.HexToAddress("0x5e227AD1969Ea493B43F840cfF78d08a6fc17796")
var multicallContractEthBalanceSelector = "4d2301cc"
func GetBalances(addresses []string, ETHProviderURL string) ([]string, error) {
ethProvider, err := ethclient.Dial(ETHProviderURL)
if err != nil {
panic(err)
}
multicallContract, err := NewMulticallCaller(multicallContractAddress, ethProvider)
if err != nil {
panic(err)
}
fmt.Println("multicallContract: ", multicallContract)
var calls = []MulticallCall{}
for _, address := range addresses {
hashAddress := common.HexToHash(address)
call := MulticallCall{multicallContractAddress, []byte("0x"+multicallContractEthBalanceSelector+hashAddress.String()[2:])}
calls = append(calls, call)
}
fmt.Println(string(calls[0].CallData))
var results []byte
err = multicallContract.contract.Call(&bind.CallOpts{}, results, "aggregate", common.Hex2Bytes(addresses[0]))
if err != nil {
panic(err)
}
fmt.Println("Result: ", &results)
return nil, nil
}
like this:
multicall.GetBalances([]string{"<MY_ADDRESS>"}, "<INFURA_API_KEY>")
And it returns this error:
# github.com/mteam88/keyswarm/multicall
multicall/multicall.go:37:58: cannot use results (variable of type []byte) as type *[]interface{} in argument to multicallContract.contract.Call
It seems like results should be a specific type/interface (maybe []uint8) but I can't seem to pass it like that to the Call function.
I have tried tons of things and I can't seem to get it. My Google diving has uncovered this Why am I getting a compile error 'cannot use ... as type uint8 in argument to ...' when the parameter is an int which led me to my previous assumption.
If you -1 please comment (:
Please provide links to relevant documentation in your answer.
NOTE: https://github.com/mteam88/keyswarm/blob/multicall/multicall/multicallContract.go is the location of the defenitions of MulticallCall and other Multicall prefixed defenitions.
I pasted a section of code that was supposed to catch an AllTopologyNodesDownError error which doesn't work and I have no idea why.
func (sc *ServerConfig) addNodesToCluster(store *ravendb.DocumentStore) error {
clusterTopology, err := sc.getClusterTopology(store)
if errors.Is(err, &ravendb.AllTopologyNodesDownError{}) {
for _, url := range sc.Url.List {
err = addNodeToCluster(store, url)
if err != nil {
return err
}
}
} else if err != nil {
return err
}
the structure of the ravendb.AllTopologyNodesDownError is
// AllTopologyNodesDownError represents "all topology nodes are down" error
type AllTopologyNodesDownError struct {
errorBase
}
type errorBase struct {
wrapped error
ErrorStr string
}
screen shot of the error when debugging the code
errors.Is() is used to tell if any error in the chain is the same instance as the provided error1, that can never be the case here because you provided a literal of your error type, no other code could hold that instance or a reference to it.
Your error looks like a type, to tell if any error in the chain is a given type you should use errors.As():
clusterTopology, err := sc.getClusterTopology(store)
var errAllDown *AllTopologyNodesDownError
if errors.As(err, &errAllDown) {
// err had an *AllTopologyNodesDownError in its
// chain and errAllDown now contains it.
}
Can be overridden by implementing the Unwrap() interface which your error type does not.
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.
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)
}
}
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.
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.
What's the best signature for a function that returns an optional value and a possible error?
For example:
func findColor(name string) (RGB, error) {
...
}
(The empty RGB value is black, a valid color, so you can't use it to infer that no value was found. Assume the error might come from something like a database connection.)
The two options that seem best are a boolean return value:
func findColor(name string) (RGB, bool, error) {
...
}
c, ok, err := findColor(myname)
if !ok {
...
} else if err != nil {
...
}
...
Or a special error value:
var ColorNotFound = errors.New(...)
func findColor(name string) (RGB, error) {
...
}
c, err := findColor(...)
if err == ColorNotFound {
...
} else if err != nil {
...
}
...
(Making special errors seems like a pain.)
What's the most idiomatic approach?
The convention in Go is to return (value, error) and if error != nil then value is (or may be) invalid.
If you have special errors you need to do something with (like io.EOF) then making a specific error is normal practice. So I would say your 3rd example is the most idiomatic, if you want to do something different for ColorNotFound.
var ColorNotFound = errors.New(...)
func findColor(name string) (RGB, error) {
// ...
}
c, err := findColor(...)
if err == ColorNotFound {
// Do something special if ColorNotFound...
} else if err != nil {
// Some other kind of error...
}
You could make findColor return *RGB and then compare it to nil:
c, err := findColor(name)
if err != nil { /* Handle error. */ }
if c == nil { /* Handle no color. */ }
This is unsafe though, since if you try to call methods on a nil pointer, they can cause a panic.
I'd recommend sticking with a special ErrColorNotFound approach.
Is there a more built-in wrapper to make a function that returns (X, error) successfully execute or abort, like regexp.MustCompile?
I'm talking about something like this, but more "built-in".
There is not. The best you'll get is something like this:
func Must(fn func() (interface{}, error)) interface{} {
v, err := fn()
if err != nil {
log.Fatalln(err)
}
return v
}
Then to use it:
Must(func() (interface{}, error) {
return template.ParseGlob(pattern)
}).(*template.Template)
Assuming that template.ParseGlob(pattern) is the call you wanted to wrap.
Go does not have parametric polymorphism, so this kind of code will end up requiring type assertions to restore the original type and so (in my opinion) is more effort than it's worth. The tidiest, idiomatic error handling you'll get for long chains of potential failure is simply to check for an error, and return it. Defer your cleanup handlers:
func MyFunc() (err error) {
a, err := blah1()
if err != nil {
return
}
defer a.Close()
b, err := blah2(a)
if err != nil {
return
}
defer b.Close()
// ad nauseam
}
Long and tedious, but at least it's explicit and easy to follow. Here are two modules I wrote that are crying out for parametric polymorphism that might give you some ideas for dealing without it:
bitbucket.org/anacrolix/dms/futures
bitbucket.org/anacrolix/dms/cache
Since Go 1.18 we can define typed Must instead of interface{}:
func Must[T any](obj T, err error) T {
if err != nil {
panic(err)
}
return obj
}
How to use: https://go.dev/play/p/ajQAjfro0HG
func success() (int, error) {
return 0, nil
}
func fail() (int, error) {
return -1, fmt.Errorf("Failed")
}
func main() {
n1 := Must(success())
fmt.Println(n1)
var n2 int = Must(fail())
fmt.Println(n2)
}
Must fails inside main, when fail() returns non-nil error
You can even define Mustn for more than 1 return parameter, e.g.
func Must2[T1 any, T2 any](obj1 T1, obj2 T2, err error) (T1, T2) {
if err != nil {
panic(err)
}
return obj1, obj2
}
I don't think a built-in mechanism would make sense since you could very well handle a non-nil error in various ways, as does the examples in the template package itself: see "text/template/examplefiles_test.go", illustrating 2 different usage of 'err':
// Here starts the example proper.
// T0.tmpl is the first name matched, so it becomes the starting template,
// the value returned by ParseGlob.
tmpl := template.Must(template.ParseGlob(pattern))
err := tmpl.Execute(os.Stdout, nil)
if err != nil {
log.Fatalf("template execution: %s", err)
}
// Output:
// T0 invokes T1: (T1 invokes T2: (This is T2))
In the particular case of the helper function (*Template) Must(), transforming an error into an exception (panic) isn't always the right course for all go programs (as debated in this thread), and to cover all the possible way to handle an error would mean to create a lot of "built-in" mechanisms.
I have encountered the same problem myself and decided to develop the following solution: https://github.com/boramalper/must
Example:
database := must.MV(sql.Open("sqlite3", "...")).(*sql.DB)
defer must.M(database.Close())
must.M(database.Ping())
// Use MustValVoid (or MVV shortly) if you don't care about
// the return value.
must.MVV(database.Exec(`
PRAGMA foreign_key_check;
...
`))
I am not sure why all the answers here are using the log package, when the
source itself uses panic:
func MustCompile(str string) *Regexp {
regexp, err := Compile(str)
if err != nil {
panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
}
return regexp
}
My recommendation would be instead of a generic Must wrapper, just implement
Must variants as needed in your code.
https://github.com/golang/go/blob/go1.16.5/src/regexp/regexp.go#L305-L314