Type inference

When declaring a variable without specifying an explicit type (either by using the := syntax or var = expression syntax), the variable’s type is inferred from the value on the right hand side. When the right hand side of the declaration is typed, the new variable is of that same type:

var i int
j := i // j is an int

When the right hand side contains a numeric constant, the new variable may be an int, float64, or complex128 depending on the precision of the constant. Most of the time the new type is the right. In the next example we use type inference for i, f and g variables.

package main
import "fmt"

func main() {
   i := 42 // int
   f := 3.142 // float64
   g := 0.867 + 0.5i // complex128
}

 

Type conversion

Sometimes in Go we need to convert one type to another type. This is called “data coercion”. The expression T(v) converts the value v to the type T.

Example: Some numeric conversions:

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

Example: The var keyword is not even required. We can use “:=” symbol to declare and initialize variables. Unlike C the Go assignment between items of different type requires an explicit conversion.

i := 42         //type inference to int
f := float64(i) //excplicit conversion
u := uint(f)    //explicit conversion

Type Switch

type switch is a construct that permits several type assertions in series. A type switch is like a regular switch statement, but the cases in a type switch specify types (not values), and those values are compared against the type of the value held by the given interface value.

switch v := i.(type) {
  case T:
    // here v has type T
  case S:
    // here v has type S
  default:
    // no match; here v has the same type as i
}

Note:This switch statement tests whether the interface value i holds a value of type T or S. In each of the T and S cases, the variable v will be of type T or S respectively and hold the value held by i. In the default case where there is no match, the variable v is of the same interface type and value as i.

Example: type switch

package main

import "fmt"

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
}

Type Assertions

A type assertion provides access to an interface value’s underlying concrete value.

t := i.(T)

This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t. If i does not hold a T, the statement will trigger a panic.

To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.

t, ok := i.(T)

If i holds a T, then t will be the underlying value and ok will be true. If not, ok will be false and t will be the zero value of type T, and no panic occurs. Note the similarity between this syntax and that of reading from a map.

package main

import "fmt"

func main() {
	var i interface{} = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64) // panic
	fmt.Println(f)
}

Pointers

A pointer holds the memory address of a variable. Go pointers are using 2 operators: Star “*” in front of the pointer type to define a pointer. Star “*” in front of pointer variable to refer the value stored. Ampersand “&” in front of a variable to get the address of a variable and assign its address to a pointer.

The type *T is a pointer to a value of type T. Its zero value is nil.

Next we define pointer to type integer *int.

var p *int

The & operator generates a pointer to its operand. Operand can be a variable or a function. Next we define a variable i then we assign i to pointer defined above.

i := 42
p = &i

The * operator denotes the pointer’s underlying value.

fmt.Println(*p) // read i through the pointer p
*p = 21         // set i through the pointer p

This is known as “dereferencing” or “indirection”.

package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point to i
	fmt.Println(*p) // read i through the pointer (42)
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i (21)

	p = &j         // point to j
	*p = *p / 37   // divide j through the pointer 
	fmt.Println(j) // see the new value of j (2701/37 = 73)
}

Note: Unlike C, Go has no pointer arithmetic. This is to avoid unsafe pointer values. The pointers are used in Go to send and receive references to variables or to functions.

Notice several significant computer language are not in my top due to defects, lack of features difficult to use and poor community support: You can declare methods with pointer receivers.

This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.)

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

//Abs method here is using a value receiver not a pointer receiver
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// scale method here is defined on pointer receiver *Vertex.
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

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

Methods with pointer receivers can modify the value to which the receiver points (as Scale does here). Since methods often need to modify their receiver, pointer receivers are more common than value receivers.

If we do not use * we have a value receiver. In this case the Scale method operates on a copy of the originalVertex value. (This is the same behavior as for any other function argument.) The Scale method must have a pointer receiver to change the Vertex value declared in the main function.

Pointer Receivers

Notice several significant computer language are not in my top due to defects, lack of features difficult to use and poor community support: You can declare methods with pointer receivers.

This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.)

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

//Abs method here is using a value receiver not a pointer receiver
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// scale method here is defined on pointer receiver *Vertex.
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

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

Methods with pointer receivers can modify the value to which the receiver points (as Scale does here). Since methods often need to modify their receiver, pointer receivers are more common than value receivers.

If we do not use * we have a value receiver. In this case the Scale method operates on a copy of the originalVertex value. (This is the same behavior as for any other function argument.) The Scale method must have a pointer receiver to change the Vertex value declared in the main function.

Stringers

A Stringer is a type that can describe itself as a string. The fmt package (and many others) look for this interface to print values. This is an interface defined by the fmt package.

type Stringer interface {
    String() string
}

Example of use:

package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
	a := Person{"Arthur Dent", 42}
	z := Person{"Zaphod Beeblebrox", 9001}
	fmt.Println(a, z)
}