Sage-Code Laboratory
index<--

Go Syntax

Go syntax is derived from C. It is a curly braced language. The symbols, operators and punctuation are similar to C. Go is using static types with type inference. That means we can create variables without declaring an explicit type.

Example: "hello world" program.

package main

//import the format package
import "fmt"

func main() {
   fmt.Println("Hello World")
}

Statements

Variables

The var statement declares a list of variables. In variable declaration the type name is specified last. This is good since the type is optional. A var statement can be at package or function level. So we can have global variables and local variables.

Example.

//variables.go
package main

import "fmt"

var i, j int = 1, 2

func main() {
    var (
        c = true;
        p = false; 
        g = "no";
    )
	fmt.Println(i, j, c, p, g)
}

Initial value

A var declaration can include one initializer for each variable. Initializer is usually a constant literal. If an initializer is present, the type can be omitted. The variable will take the type of the initializer. Read the example below to understand better this concept:

//file var_list.go
package main

import "fmt"

var i, j int = 1, 2

func main() {
    var c , p, g = true, false, "no"
	fmt.Println(i, j, c, p, g)
}

Constants

In Go it is good to use constants instead of regular variables to define numbers or other values that will not change during program execution.

//file pub_con.go
package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

Numeric Constants

Numeric constants are high-precision values. An untyped constant takes the type needed by its context using type inference.

//file huge_con.go
package main

import "fmt"

const (
	// Create a huge number by shifting a 1 bit left 100 places.
	// In other words, the binary number that is 1 followed by 100 zeroes.
	Big = 1 << 100
	// Shift it right again 99 places, so we end up with 1<<1, or 2. 
    Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x * 0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

Keywords

The following keywords are reserved and may not be used as identifiers.

breakdefault func else select
case defer go map struct
chan interface goto packageswitch
constfallthroughif range type
for continue importreturn var

Operators

The following character sequences represent operators (including assignment operators) and punctuation:


+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=

Packages

Every Go program is made up of packages. Programs start running in package "main". The main package also contains the function main(). Using the import statement one package can be used into another package. By convention, the package name is the same as the last element of the import path.

//file imp_pack.go
package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

In this instance, th package "math/rand" comprises files that begin with the statement "package rand".

Import

This code groups the imports into a parenthesis, "factored" import statement.

You can also write multiple import statements, like:

import "fmt"
import "math"

But it is good style to use the factored import statement.

Public elements

In Go, a name is exported automaticly if it begins with a capital letter. This is disruptive convention that makes Go different than Java. Where in Java class names start with capital letter, but Go do not have classes.

For example the "println()" function is using capital P so is fmt.Println() instead of fmt.println(). When the package "fmt" is imported only the capital letter functions and elements are accessible using dot notation.

Note: This convention is a bit strange but you get use to it.

Classes

Go is object oriented language and has support for creation of objects that have methods and properties. However Go do not have classes like Java does. It has only types like Ada language.

Methods

A method is a function with a special receiver argument. This argument has the role of "this" or "self" that you can use in Java or Python. The receiver appears in its own argument list between the func keyword and the method name.

You can define methods on user defined types by using receiver argument. Types with methods can be based on collections, structures or simple types. In this way a type becomes like class.

Example:

In next example, the Abs method has a receiver of type Vertex.The method Abs() can be called using dot notation with the type name used as a qualifier like this v.Abs().

//file class.go
package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}

Objects

In Java we use keyword "new" to create an instance of a class. In Go we do not have keyword new and there are no classes, but there is instante of a type that is an object.

Interfaces

An interface is an abstract data type. It define of a set of methods that must be implemented for a type that implement the interface. A value of interface type can hold any value that implements those methods.

//file interface.go
package main

import (
	"fmt"
	"math"
)

//define an interface
type Abser interface {
	Abs() float64
}

func main() {
	var a Abser

	//define a function f
	f := MyFloat(-math.Sqrt2)
	v := Vertex{3, 4}

	a = f  // a MyFloat implements Abser
	a = &v // a *Vertex implements Abser

	// In the following line, v is a Vertex (not *Vertex)
	// therefore does NOT implement Abser.
	a = v

	fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

Note: There is an error in the example code on line 22. Vertex (the value type) doesn't implement Abser because the Abs method is defined only on *Vertex (the pointer type).

Empty Interface

The interface type that specifies zero methods is known as the empty interface: interface{}

An empty interface may hold values of any type. (Every type implements at least zero methods.)

Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print takes any number of arguments of type interface{}.

//file empty_interface.go
package main

import "fmt"

func main() {
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

Interface Values

Under the covers, interface values can be thought of as a tuple of a value and a concrete type (value, type). An interface value holds a value of a specific underlying concrete type. Calling a method on an interface value executes the method of the same name on its underlying type.

package main

import (
	"fmt"
	"math"
)

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	fmt.Println(t.S)
}

type F float64

func (f F) M() {
	fmt.Println(f)
}

func main() {
	var i I

	i = &T{"Hello"}
	describe(i)
	i.M()

	i = F(math.Pi)
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

Read next: Data Types