Home > OS >  Global Vars in Golang
Global Vars in Golang

Time:12-23

I've been reading a book about Golang and it's common use global vars to expose the result of private functions. Why not to expose public functions? here's an exemple. I am comming from java and this is tricky for me. What are advantages of using global vars like these?

EDIT: I notice that without parenthesis I am not calling the function. It's just a pointer. Is it a good pratice or does have some advantages?

package config

import "time"

var (
    
    GetHTTPServerAddress = getHTTPServerAddress
    GetHTTPReadTimeout = getHTTPReadTimeout
    GetHTTPWriteTimeout = getHTTPWriteTimeout
)

func getHTTPServerAddress() string {
    return getConfigString("http.server_address")
}

func getHTTPReadTimeout() time.Duration {
    return getConfigDuration("http.read_timeout")
}

func getHTTPWriteTimeout() time.Duration {
    return getConfigDuration("http.write_timeout")
}

CodePudding user response:

I've been reading a book about Golang and it's common use global vars to expose the result of private functions.

To expose the result of private functions, not the functions themselves; your example is a little off. Without the parens you're exposing the function itself.

package main

import (
    "fmt"
)

var(
    FooItself = foo      // (func() string)(0x108b720)
    FooResult = foo()    // "bar"
)

func main() {
    fmt.Printf("%#v\n", FooItself)
    fmt.Printf("%#v\n", FooResult)
}

func foo() string {
    return "bar"
}

Try it.

Your example would look more like this.

package config

var (
    // You wouldn't call the variable "Get*", that exposes
    // the implementation.
    HTTPServerAddress = getHTTPServerAddress()
    HTTPReadTimeout = getHTTPReadTimeout()
    HTTPWriteTimeout = getHTTPWriteTimeout()
)

func getHTTPServerAddress() string {
    return getConfigString("http.server_address")
}

func getHTTPReadTimeout() time.Duration {
    return getConfigDuration("http.read_timeout")
}

func getHTTPWriteTimeout() time.Duration {
    return getConfigDuration("http.write_timeout")
}

But no private methods are necessary. Just run the function and assign it to a global.

package config

var (
    HTTPServerAddress = getConfigString("http.server_address")
    HTTPReadTimeout = getConfigDuration("http.read_timeout")
    HTTPWriteTimeout = getConfigDuration("http.write_timeout")
)

Now people can use config.HTTPServerAddress and the details remain hidden without requiring an extra layer of methods.


And yes, it can be done on the result public functions. A very common example is error codes. For example, in net/http.

var (
    ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
    ErrHijacked = errors.New("http: connection has been hijacked")
)

In Java you might create an exception subclass for each type of error. In Go you make a single object, share it as a global variable, and everything references that object.

The purpose is to hide the details from the user. The exact wording of the error can change, but as long as everyone is referencing net.http.ErrBodyNotAllowed their code will still work.


io/fs has a more complex example exposing the result of its private methods.

var (
    ErrInvalid    = errInvalid()    // "invalid argument"
    ErrPermission = errPermission() // "permission denied"
    ErrExist      = errExist()      // "file already exists"
    ErrNotExist   = errNotExist()   // "file does not exist"
    ErrClosed     = errClosed()     // "file already closed"
)

func errInvalid() error    { return oserror.ErrInvalid }
func errPermission() error { return oserror.ErrPermission }
func errExist() error      { return oserror.ErrExist }
func errNotExist() error   { return oserror.ErrNotExist }
func errClosed() error     { return oserror.ErrClosed }

Why use private method wrappers here? Why not ErrInvalid = oserror.ErrInvalid?

(I'm assuming) Go wants to keep oserror hidden, even from the documentation!

oserror is just making error objects to define its possible errors.

package oserror

import "errors"

var (
    ErrInvalid    = errors.New("invalid argument")
    ErrPermission = errors.New("permission denied")
    ErrExist      = errors.New("file already exists")
    ErrNotExist   = errors.New("file does not exist")
    ErrClosed     = errors.New("file already closed")
)

CodePudding user response:

If it's result of a function, it's probably something like static member initialization or perhaps even static final.

The example you linked suggests that it's some sort of config load procedure and values are exposed for global use.

Precomputing data is also not unheard of.

If you see function pointers, however, it could be done to enable mocking.

YMMV. Sometimes it's just an antipattern as well.

  •  Tags:  
  • go
  • Related