Can Go's linker override initialized variables - go

From ld's docs:
-X symbol value
Set the value of an otherwise uninitialized string variable. The symbol name should be of the form importpath.name, as displayed in the symbol table printed by "go tool nm".
So this is pretty cool. It allows you to do stuff like this:
package main
import "fmt"
var version string
func main() {
fmt.Println(version)
}
Compile with: go build -ldflags '-X main.version 42' ...
I have two question about his feature. First of all it also works for initialized strings (e.g. var version = "bad build") even though the documentation specifically says "otherwise uninitialized string variable".
The seconds question is about spaces. My Makefile contains the following lines:
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
LDFLAGS := '-X main.version "$(GIT_BRANCH) $(GIT_COMMIT)"'
The documentation for the go command says:
-ldflags 'flag list'
So they're using single quotes for all linker flags. But what about a string containing spaces as symbol for the -X flag? The double-quotes work just fine, so do escaped single-quotes btw., I'm just not sure I can rely on all that to work consistently given the docs not mentioning any of it.
Clarification of the first question:
Go zero-initializes all vars.
The documentation says: -X symbol value Set the value of an otherwise uninitialized string variable [...].
Does this mean:
var foo string // only this one?
var bar = "bar" // or this one too, maybe

The quotes are handled by the shell (or make), so yes it's consistent.
The calling program populates go's args.
//edit
To use a default version you can use something like this:
var version string
func init() {
if len(version) == 0 {
version = "master"
}
}
//edit 2
From the spec:
When memory is allocated to store a value, either through a
declaration or a call of make or new, and no explicit initialization
is provided, the memory is given a default initialization. Each
element of such a value is set to the zero value for its type: false
for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil
for pointers, functions, interfaces, slices, channels, and maps.

Related

Why Go returns value 0 to an unassigned integer variable?

I was asked to declare a variable of integer type as:
var someInteger int8
Later when I printed this variable, it prints the value 0.
My Go Program looks like:
package main
import "fmt"
func main() {
var someInteger int
fmt.Println(someInteger) // Prints 0 in terminal
}
My question is since I haven't assigned any value, so it should return some Garbage value like C instead of behaving like static variables, which automatically initialize by value 0.
In Go you can't access uninitialized variables / memory. If you don't initialize a variable explicitly, it will be initialized implicitly to the zero value of its type.
This is covered in Spec: Variable declarations:
If a list of expressions is given, the variables are initialized with the expressions following the rules for assignments. Otherwise, each variable is initialized to its zero value.
Also mentioned at Spec: Variables:
If a variable has not yet been assigned a value, its value is the zero value for its type.
And also covered in the Go Tour: Zero Values which I highly recommend to take if you're learning the language.
Variables declared without an explicit initial value are given their zero value.
Go makes this thing easy by adding sensible default values based on the type of the variables. For example:
var someInteger int8 // will print 0 as default
var someFloat float32 // will print 0 as default
var someString string // will print nothing as it prints empty string
var someBoolean bool // will print false as default
As #icza mentioned in his answer you can read more about it here

How can I access DOM Element properties from Go WebAssembly?

I'm trying to extend the "Hello WebAssembly" example from https://github.com/golang/go/wiki/WebAssembly. As given, the example simply prints a message to the console. I wanted to add some code using syscall/js to replace the body element content.
The attempt below fails to build:
package main
import (
"fmt"
"syscall/js"
)
func main() {
fmt.Println("Hello, WebAssembly!") // original example
// I added
doc := js.Global().Get("document")
body := doc.Call("getElementById", "thebody")
body.innerHTML = "Dynamic Content"
}
When I try to build with $ env GOOS=js GOARCH=wasm go build -o main.wasm
I get :
./wasm.go:14:6: body.innerHTML undefined (type js.Value has no field or method innerHTML)
Not surprising when you think about it, but I don't see an example in the doc at https://godoc.org/syscall/js that explains how to get and set element properties.
To get the value of any property of some JavaScript object, use the Value.Get() method (you actually already used it when you accessed the document object by calling js.Global().Get("document")). Similarly, to set a value of a property, use Value.Set().
The name of the property whose value to get / set is simply a Go string value, "innerHTML" in your case. The value to be set may be many of Go values (e.g. string, integer numbers, floating point numbers, bool, slices, maps etc.), the js.ValueOf() function is used to obtain a js.Value() that will be set ultimately. In your case, you may simply use the Go string value "Dynamic Content".
doc := js.Global().Get("document")
body := doc.Call("getElementById", "thebody")
body.Set("innerHTML", "Dynamic Content")

Why can't I pass a pointer to fmt.Println using flag.StringVar?

I've started looking at Golang, and following an example to pass in command line arguments, I've got this code:
package main
import (
"flag"
"fmt"
)
func main() {
wordPtr := flag.String("namestring", "stringvalue", "Pass in a string")
numPtr := flag.Int("number", 11, "Pass in an int")
boolPtr := flag.Bool("switchflag", false, "Pass in a bool")
var svar string
flag.StringVar(&svar, "svar", "svarstringvalue", "A string variable")
flag.Parse()
fmt.Println("Word:", *wordPtr)
fmt.Println("Word2:", wordPtr)
fmt.Println("numb:", *numPtr)
fmt.Println("numb2", numPtr)
fmt.Println("switchflag", *boolPtr)
fmt.Println("switchflag2", boolPtr)
fmt.Println("svar:", svar)
fmt.Println("svar2", &svar)
fmt.Println("svar3", *svar) //<-- This won't work...
fmt.Println("tail:", flag.Args())
}
My question is, why do I get the value of the other pointers if I dereference it (like *wordPtr etc), whereas if I pass wordPtr in, I get the memory address, but with svar, I have to pass in svar to get the value, and &svar gets me the memory address?
Looking at the documentation here https://golang.org/pkg/flag/, it says that you pass in a variable to store the flag (presumably a variable that is already created?), but I still don't understand why I have to do something different to it to get the value, when compared with other variables.
This is the result of me running it with passing in some commands:
➜ Documents go run main.go -number 42 -switchflag -svar testtringvalue
Word: stringvalue
Word2: 0xc42005c1e0
numb: 42
numb2 0xc4200160a0
switchflag true
switchflag2 0xc4200160a8
svar: testtringvalue
svar2 0xc42005c1f0
tail: []
Any help is appreciated. Also, if I've got any understanding of it wrong, please let me know.
Cheers.
Well this is fairly simple, you are trying to dereference a value instead of pointer.
var svar string is not a pointer, but a value.
fmt.Println("svar3", svar) is correct use in this case
Don't worry about about use of StringVar as
...Var(&svar, "sv... means that you are passing address of svar not a value.

Idiomatic way to initialise an empty string in Go

In Go you can initialise an empty string (in a function) in the following ways:
var a string
var b = ""
c := ""
As far as I know, each statement is semantically identical. Is one of them more idiomatic than the others?
You should choose whichever makes the code clearer. If the zero value will actually be used (e.g. when you start with an empty string and concatenate others to it), it's probably clearest to use the := form. If you are just declaring the variable, and will be assigning it a different value later, use var.
var a string
It's not immediately visible that it's the empty string for someone not really fluent in go. Some might expect it's some null/nil value.
var b = ""
Would be the most explicit, means everyone sees that it's a variable containing the empty string.
b := ""
The := assigment is so common in go that it would be the most readable in my opinion. Unless it's outside of a function body of course.
There isn't a right way to declare empty variables, but there are some things to keep in mind, like you can't use the := shortcut outside of a function body, as they can with var:
var (
name string
age int64
)
I find var name = "" to be the clearest indication that you're declaring an empty variable, but I like the type explicitness of var name string. In any case, do consider where you are declaring variables. Sometimes all at once outside the function body is appropriate, but often nearest to where you actually use it is best.
rob (Pike?) wrote on a mailthread about top-level declaration
At the top level, var (or const or type or func) is necessary: each item must be introduced by a keyword for ur-syntactic reasons related to recognizing statement boundaries. With the later changes involving semicolons, it became possible, I believe, to eliminate the need for var in some cases, but since const, type, and func must remain, it's not a compelling argument.
There is a certain ambiguity in "short-variable" declarations (using :=), as to whether the variable is declared or redeclared as outlined in the specs:
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.
There is absolutely no difference in the generated code (with the current compiler – Go 1.7.4), and also gometalinter does not complain for any of those. Use whichever you like.
Some differences:
You can only use the short variable declaration in functions.
With short variable declaration, you can create variables of multiple types in one line, e.g.
a, b := "", 0
The following 3 apps generate identical code:
a.go
package main
import "fmt"
func main() { var a string; fmt.Println(a) }
b.go
package main
import "fmt"
func main() { var a = ""; fmt.Println(a) }
c.go
package main
import "fmt"
func main() { a := ""; fmt.Println(a) }
To verify, build each (e.g. with go build -o a), and use diff on Linux to compare the executable binaries:
diff a b && diff a c
I try to stick to the short declaration for a couple of reasons.
It's shorter
Consistency
Allocates memory for maps, slices and pointers to structs and types.
Although var a string and a := "" are the same, b := []T{} and var b []T are not the same. When dealing with slices and maps the shorter declaration will not be nil. More often then not (especially with maps) I want allocated memory.
There are few situations where var will be needed, for instance, you are calling a function that will populate a property of a type.
var err error
foo.Name, err = getFooName()
Using := syntax in the above situation will error out since foo.Name is already declared.
just
*new(string)
It's only stuff in stackoverf related to empty strings in go. So it should be here

Explanation of Flags in Go

Can anyone explain flags in Go?
flag.Parse()
var omitNewline = flag.Bool("n", false, "don't print final newline")
flags are a common way to specify options for command-line programs.
package main
import (
"flag"
"fmt"
)
var (
env *string
port *int
)
// Basic flag declarations are available for string, integer, and boolean options.
func init() {
env = flag.String("env", "development", "a string")
port = flag.Int("port", 3000, "an int")
}
func main() {
// Once all flags are declared, call flag.Parse() to execute the command-line parsing.
flag.Parse()
// Here we’ll just dump out the parsed options and any trailing positional
// arguments. Note that we need to dereference the points with e.g. *evn to
// get the actual option values.
fmt.Println("env:", *env)
fmt.Println("port:", *port)
}
Run Programs:
go run main.go
Try out the run program by first giving it without flags. Note that if you omit flags they automatically take their default values.
go run command-line-flags.go --env production --port 2000
If you provide a flag with specified value then default will overwrite by passed one.
See http://golang.org/pkg/flag/ for a full description.
The arguments for flag.Bool are (name string, value bool, usage string)
name is the argument to look for, value is the default value and
usage describes the flag's purpose for a -help argument or similar, and is displayed with flag.Usage().
For more detailed example check here
flag is used to parse command line arguments. If you pass "-n" as a command line argument, omitNewLine will be set to true. It's explained a bit farther in the tutorial :
Having imported the flag package, line 12 creates a global variable to hold the value of echo's -n flag. The variable omitNewline has type *bool, pointer to bool.
Personally, I prefer the Var type functions, as they take a reference, rather
than returning a reference. That way you can use the variable without
dereferencing:
package main
import "flag"
func main() {
var omitNewline bool
flag.BoolVar(&omitNewline, "n", false, "don't print final newline")
flag.Parse()
println(omitNewline)
}
https://golang.org/pkg/flag#BoolVar

Resources