Why can't you name a function in Go "init"? - go

So, today while I was coding I found out that creating a function with the name init generated an error method init() not found, but when I renamed it to startup it all worked fine.
Is the word "init" preserved for some internal operation in Go or am I'm missing something here?

Yes, the function init() is special. It is automatically executed when a package is loaded. Even the package main may contain one or more init() functions that are executed before the actual program begins: http://golang.org/doc/effective_go.html#init
It is part of the package initialization, as explained in the language specification: http://golang.org/ref/spec#Package_initialization
It is commonly used to initialize package variables, etc.

You can also see the different errors you can get when using init in golang/test/init.go
// Verify that erroneous use of init is detected.
// Does not compile.
package main
import "runtime"
func init() {
}
func main() {
init() // ERROR "undefined.*init"
runtime.init() // ERROR "unexported.*runtime\.init"
var _ = init // ERROR "undefined.*init"
}
init itself is managed by golang/cmd/gc/init.c:
Now in cmd/compile/internal/gc/init.go:
/*
* a function named init is a special case.
* it is called by the initialization before
* main is run. to make it unique within a
* package and also uncallable, the name,
* normally "pkg.init", is altered to "pkg.init·1".
*/
Its use is illustrated in "When is the init() function in go (golang) run?"

Related

How to initialize global variables of a package in a lazy manner, from the user input?

There're multiple packages in my projects: the main one and secondary ones. The main one accepts input from the users and initializes some variables in itself.
The secondary packages contain some global variables that can only be initialized once the one of the "main" package have been, and not earlier.
I'm aware of the function "init()" of a package but this won't work in my case because it's not capable on initializing data in a lazy way, and with some arguments or data from the outside. Whereas this is what I want.
//main package
var (
a1 int
a2 float
a3 string
a4 MyStruct
a5 MyStruct2
)
func main() {
//process user input
//...........
//some other stuff
// I want that by this point, the global variables of package2 to have been initialized
// **based on** the processed user input from above
//namely, before the first access to the package
package2.Func1()
// I want that by this point, the global variables of package3 to have been initialized
// **based on** the processed user input from above
//namely, before the first access to the package
package3.Func11()
}
Is there a solution for this?
I could've a function "initData()" in the packages package2 and package3, but how would I ensure that it won't be forgotten to be called as the very first function, in a mandatory way, before calling any other functions of a package?
you should probably use an init function in main.go
func init(){
// get user input and set variables
// call package 2 and 3 after setting the variables in main
package2.Func1()
package3.Func1()
}
func main(){
// everything is setup properly when code in this main function is launched
}

"flag provided but not defined" error in Go test despite the flag being defined in init()?

This question is similar to go test flag: flag provided but not defined, but since that question does not contain a minimal example and the answer is quite high-level, I'm asking it again. In a Go module with a main.go and a main_test.go,
.
├── go.mod
├── go.sum
├── main.go
└── main_test.go
The main.go defines a sayHi flag:
package main
import (
"flag"
"fmt"
)
var sayHi bool
func init() {
flag.BoolVar(&sayHi, "sayHi", false, "Say hi or not")
flag.Parse()
}
func main() {
if sayHi {
fmt.Println("Hi!")
}
}
and the main_test.go is just a placeholder test:
package main
import "testing"
func TestSayHi(t *testing.T) {
}
The problem is that if I try to run the tests, I get a "flag provided but not defined" error:
> go test ./...
flag provided but not defined: -test.testlogfile
Usage of /var/folders/tp/s7nwwdws1wj0z8s0vftppnnc0000gn/T/go-build952058535/b001/my-module.test:
-sayHi
Say hi or not
FAIL github.com/kurtpeek/my-module 0.192s
FAIL
From the answer of the aforementioned question,
You have to make sure that all flag definitions happen before calling flag.Parse(), usually by defining all flags inside init() functions.
I don't see how that is not being done here?
Create test binary with following command and then execute it with your argument.
> go test -c -o test
./test --sayHi=true
PASS
go test command does not pass along the arguments to underlying tests.
Update as of May 2022 - NOT fixed
So far the recommended approach is to move flag initalisation outside of init() (so stupid, I know!)
So I did that:
moving the code for flag handling to its own function,
and then calling that function at the top of main()
and my tests stopped complaining.
func initParseCommandLineFlags() {
// do your flag thing here
}
func main() {
initParseCommandLineFlags()
...
}
NOTE: A huge down side was that I had to move all of my initialisation out of init() into other setup functions, because my program startup can be modified by command line switches.
So I don't have init anymore :(
Reference: https://github.com/golang/go/issues/46869#issuecomment-865695953

How to call unexported function within package?

I'm trying to write a package to work with. Here is sample code:
package redis
import (
"fmt"
"github.com/gomodule/redigo/redis"
"log"
"os"
)
var conn redis.Conn
func init() {
// code to set conn variable
}
func do(command string, args ...interface{}) (interface{}, error) {
init()
return conn.Do(command, args)
}
This code does not compile and compilator says undefined: init. When I change init() to Init() it works, but I don't want it to be available outside of package.
Wherever I read about this problem, it says about calling unexported function from another package, but here I'm calling it from same one.
Also, Goland IDE marks function call as unresolved reference and suggests to create it. But when I do (by IDE itself), it still doesn't see it.
init() is a special function. From the language spec:
func init() { … }
Multiple such functions may be defined per package, even within a
single source file. In the package block, the init identifier can
be used only to declare init functions, yet the identifier itself
is not declared. Thus init functions cannot be referred to from
anywhere in a program.
Use init() for package level initializations.
In Go, init is reserved for initializing work that needs to be done in a package, eg. adding some implementation to some registry.
To solve this problem, you need to use another name.
Take a look at this question to learn more about init if you are interested.

Viper, cannot get the value initialized outside the functions

I have a problem using a viper, I have assigned variable by viper method, so when I try to get value inside any function I have a null value. Does anybody have any idea why does it happen so? Any other variables initialization works fine, but not viper GetString method.
Structure:
main.go
package main
import (
"project/Model"
"github.com/spf13/viper"
...
)
func main() {
//Config handling
viper.SetConfigName("main")
viper.AddConfigPath("/config/")
err = viper.ReadInConfig()
...
}
Package Model
package Model
import ("github.com/spf13/viper"
...
)
var sqlhost = viper.GetString("db.host")
func foo() {
log.Println(sqlhost)
}
I suspect that doing package variable initialization as you show will lead to the viper configuration not being complete before performing the GetString(), and therefore the zero (empty) string value is what is returned. Without seeing how you initialize your viper config, I'm guessing that it's not in an init() function, but is in a main() function that hasn't run yet when the package variable is assigned.
You should probably retrieve the viper.GetString("db.host") inside the highest level function that needs it rather than during package initialization.

go how to check if a function exists

Is there any way to check if a func exists in Go?
In PHP I might do something like
if(function_exists('someFunction')) {
//...
}
but I have not been able to figure out how to do this in Go
Any help on this will be greatly received.
A little more context on what you're trying to do would help.
As you note in your own comment, if you try to call a function Go checks at compile-time if the function exists, most of the times.
One exception that comes to my mind is when you use interface{} and you want to check that a method exists before calling it. For this you can use type checks.
Example:
package main
import "fmt"
// a simple Greeter struct
// with a Hello() method defined
type Greeter struct {
Name string
}
func (m *Greeter) Hello() string {
return "hello " + m.Name
}
var x interface{}
func main() {
x = Greeter {Name:"Paolo"} // x is a interface{}
g,ok := x.(Greeter) // we have to check that x is a Greeter...
if ok {
fmt.Println(g.Hello()) // ...before being able to call Hello()
}
}
Another scenario I can think of is that you're creating your own tool for Go that requires parsing go files before compiling them. If so, Go provides help in the for of the parser package
There's no way to do that (and for good!). The reason is that Go is a compiled language and does not support loadable modules (yet, at least) so functions can't come and go at runtime, and hence whether or not a top-level function exists, you know by defintion: if a given source file imports a package containing the function of interest1, that function is visible in this source file. Or this function is declared in the same package this source file belongs to and hence it's also available. In all the other cases the function is not available. Note that a function might be compiled in the program but still not visible in a given source file while compiling, so the whole definition of visibility as you put it does not exist in Go.
On the other hand you might want some generality. Generality in Go is achieved via interfaces. You might define an interface and then require a type to implement it. Checking that a type implements an interface is done via a neat trick.
An update from 2021-12-29.
The support for loadable modules was added in Go 1.8 in the form of the plugin package and has since then matured to support most mainline platforms except Windows.
Still, this solution is not without problems—for instance, see #20481.
1Without renaming that module to nothing, but let's not touch this for now.
Provided your thing is an interface value, type assert, something like this:
if Aer, ok := thing.(interface{MethodA()}); ok {
Aer.MethodA()
}
If thing is a struct, assign it to an interface variable first,
because type assertions only work on interface values.
It wouldn't hurt to define a named interface instead of using the
nonce one, but for simple cases like this it's not worth it.
Recently I had a need for figuring out if a struct has a particular function or not.
Here is another way using reflection :
package main
import (
"fmt"
"reflect"
)
type FuncRegistry struct {}
func (fr FuncRegistry) Hi() {
fmt.Println("function Hi")
}
func (fr FuncRegistry) Hello() {
fmt.Println("function Hello")
}
func functionExists(obj interface{},funcName string) bool {
mthd := reflect.ValueOf(obj).MethodByName(funcName)
if mthd.IsValid() {
fmt.Printf("Function '%s' exists \n",funcName)
return true
}
fmt.Printf("Function '%s' does not exist\n",funcName)
return false
}
// Main function
func main() {
var fr FuncRegistry
functionExists(fr,"Hi")
functionExists(fr,"Hello")
functionExists(fr,"Fail")
}
This sounds a lot like a XY problem. Please tell what you are trying to do. As far as I know, this is something you can't really do the same way as in PHP.
However, you could create a map with function names as keys. Add the functions there manually or generate the contents by parsing the source files before the compilation or at run time. Parsing the source seems like a dirty hack though.

Resources