6. Detailed information

6.1 Semicolons

Like C, Go’s formal grammar uses semicolons to terminate statements, but unlike in C, those semicolons do not appear in the source.

Idiomatic Go programs have semicolons only in places such as for loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line, should you write code that way.

One consequence of the semicolon insertion rules is that you cannot put the opening brace of a control structure (if, for, switch, or select) on the next line. If you do, a semicolon will be inserted before the brace, which could cause unwanted effects. Write them like this:

if i < f() {
        g()
}

not like this:

The following is incorrect:

Wrong placement of braces


if i < f()  // wrong!
{           // wrong!
        g()
}

6.2 init

Every Go package may contain one or more init() functions specifying actions that should be taken during program initialisation. Here, multiple declarations of the same identifier can occur without either resulting in a compilation error or the shadowing of a variable.

Program: my_init.go


 1 package main
 2 
 3 import "fmt"
 4 
 5 const Hello = "Hi!"
 6 var world string
 7 
 8 func init() {
 9         world = "My world."
10 }
11 
12 func main() {
13         fmt.Println(Hello, world)
14 }

The output is:

Hi! My world.

6.3 Formatting

A Go program can be formatted in (almost) any way the programmers want. With Go, let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments.

So if your program name is hello.go then format it as follows:

go fmt hello.go

The output of the filter gofmt is the officially endorsed format.

The cool thing about Go code is that the officially endorsed format includes tabs, and so if you like having your tabs present as the traditional 8 spaces, go ahead. If you want them to present as 2 spaces, go ahead, if you want them to present as 4 spaces, or 20 spaces, you are not affecting anyone else with your preferences.

6.4 More on Strings

Strings are a sequence of UTF-8 characters (one-byte UTF-8 codes have the same value as the corresponding ASCII code). UTF-8 is the most widely used encoding, the standard encoding for text files, XML files and JSON strings. While able to represent characters that need 4 bytes, ASCII-characters are still stored using only 1 byte. A Go string is thus a sequence of variable-width characters (each 1 to 4 bytes), contrary to strings in other languages as C++, Java or Python that are fixed-width (Java uses always 2 bytes).

The advantages are that Go strings and text files occupy less memory/disk space, and since UTF-8 is the standard, Go does not need to encode and decode strings as other languages have to do. Strings are value types and immutable: once created you cannot modify the contents of the string.

A Go string is a sequence of variable-width characters where each character is represented by one or more bytes, normally using the UTF-8 encoding. Strings are length-delimited and not terminated by a special character as in C/C++.

The initial (default) value of a string is the empty string "".

String literals have type string. String literals are of two types:

  • Interpreted strings i.e. surrounded by "" (double quotes).
  • Raw strings i.e. surrounded by back quotes.

They are not interpreted; they can span multiple lines. In the literal `This is a raw string \n` the \n is not interpreted but taken literally.

Remember:

Strings can’t be nil

package main

func main() {  
        var x string = nil //error

        if x == nil { //error
                x = "default"
        }
}

The compiler error is:

main.go:4: cannot use nil as type string in assignment

Strings are not always UTF8 text

String values are not required to be UTF8 text. They can contain arbitrary bytes. To know if you have a UTF8 text string use the ValidString() function from the unicode/utf8 package.

~~~~~~~~ package main

import (
“fmt” “unicode/utf8” )

func main() {
data1 := “ABC” fmt.Println(utf8.ValidString(data1)) //prints: true

1     data2 := "A\xfeC"
2     fmt.Println(utf8.ValidString(data2)) //prints: false

}

Remember contd.:

String length

The built-in len() function returns the number of bytes instead of the number of characters like it’s done for unicode strings in say the Python language.

6.4.1 String functions

The standard library’s strings1 package provides many useful string-related functions. Here are a few in action:

Program: my_string.go


 1 package main
 2 
 3 import "fmt"
 4 import s "strings"
 5 
 6 // We alias fmt.Println to a shorter name
 7 var p = fmt.Println
 8 
 9 func main() {
10         // Note that these are all functions from package, not methods on the
11         // string object itself. This means that we need pass the string in
12         // question as the first argument to the function.
13         p("ToLower:   ", s.ToLower("TEST"))
14         p("ToUpper:   ", s.ToUpper("test"))
15         p("Split:     ", s.Split("a-b-c-d-e", "-"))
16         p("Index:     ", s.Index("test", "e"))
17         p("Contains:  ", s.Contains("test", "es"))
18         p("Count:     ", s.Count("test", "t"))
19 }

The output is:

ToLower:    test
ToUpper:    TEST
Split:      [a b c d e]
Index:      1
Contains:   true
Count:      2

6.5 defer

Go has a special statement called defer which schedules a function call to be run after the function completes.

defer is often used when resources need to be freed in some way. For example when we open a file we need to make sure to close it later. With defer:

1 file, err := os.Open("hello.go")
2 check(err)
3 defer file.Close()

This has 3 advantages:

  • it keeps our Close call near our Open call so its easier to understand,
  • if our function had multiple return statements (perhaps one in an if and one in an else) Close will happen before both of them and
  • deferred functions are run even if a run-time error occurs.

Another example showing that defer runs when the function returns and not when the scope is exited Also, that defer’ed function calls are run in LIFO order:

Program: my_defer.go


 1 package main
 2 
 3 import "fmt"
 4 
 5 func f() {
 6         for i := 0; i < 5; i++ {
 7                 defer func(n int) { fmt.Println("bye", n) }(i)
 8                 fmt.Println(i)
 9         }
10 }
11 
12 func main() {
13         f()
14 }

The output is:

0
1
2
3
4
bye 4
bye 3
bye 2
bye 1
bye 0

6.6 Using the log package

Usage: import "log"

Package log implements a simple logging package. It defines a type, Logger, with methods for formatting output. It also has a predefined ‘standard’ Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and Panic[f|ln], which are easier to use than creating a Logger manually. That logger writes to standard error and prints the date and time of each logged message. The Fatal functions call os.Exit(1) after writing the log message. The Panic functions call panic after writing the log message.

Example: log.Fatal(err)

6.7 Using the big package

Package big implements multi-precision arithmetic (big numbers). The following numeric types are supported:

  • Int signed integers
  • Rat rational numbers

func NewInt(x int64) *Int

NewInt allocates and returns a new Int set to x.

func (z *Int) Mul(x, y *Int) *Int

Mul sets z to the product x*y and returns z.

Here’s an example:

Program: bigex.go


 1 package main
 2 
 3 import (
 4         "fmt"
 5         "math/big"
 6 )
 7 
 8 func main() {
 9         verybig := big.NewInt(1)
10         ten := big.NewInt(10)
11 
12         for i:=0; i<10000; i++ {
13                 temp := new(big.Int)
14                 temp.Mul(verybig, ten)
15                 verybig = temp
16         }
17 
18         fmt.Println(verybig)
19 }

The output is:

1000.....

Note: If you want it to run fast enough for Go Playground, use a smaller exponent than 100000.

6.8 go vet

go vet2 is used for analysing Go code and finding common mistakes. Among many it checks for some common mistakes like:

  • Useless assignments
  • Comparing functions with nil
  • Using wrong printf format specifiers
  • Closing over loop variables the wrong way
  • Unreachable code
  • Mistakes involving boolean operators

6.9 golint

Go is a tool3 for managing Go source code.

Usage: go command [arguments] where get is one of the many commands available.

go get downloads and installs the packages named by the import paths, along with their dependencies.

Install golint with go get github.com/golang/lint/golint

While go vet checks your code for actual programming errors, golint checks your code for style violations.

Golint does not emit errors or warnings, but “suggestions”: These suggestions can be wrong at times, and code that golint complains about isn’t necessarily wrong – it might just be hitting a false positive. Nevertheless, it’s more often right than wrong, and you should definitely run golint on your code from time to time and fix those suggestions that it is right about.

6.10 goimports

Install goimports with go get golang.org/x/tools/cmd/goimports

Do you wish Go would just allow you to import unused packages? Or are you even tired of having to manually import all those packages you use? Then goimports might be for you. It’s a replacement for gofmt that, in addition to formatting your code, it also manages your imports for you.

It removes unused imports and adds missing imports.

Usage: goimports -w go_program.go

6.11 Naming Convention Summary

There is no detailed coding style guide for Golang.

Here are some references that would help:

  1. Go Code Review Comments4
  2. How to write Go code5
  3. Godoc: Documenting Go code6
  4. Effective Go7

Between gofmt, golint and go vet, you cover already a lot of conventions.

  1. http://golang.org/pkg/strings/
  2. https://golang.org/cmd/go/#hdr-Run_go_tool_vet_on_packages
  3. https://golang.org/cmd/go/
  4. https://github.com/golang/go/wiki/CodeReviewComments
  5. https://golang.org/doc/code.html
  6. http://blog.golang.org/godoc-documenting-go-code
  7. https://golang.org/doc/effective_go.html