golang

Project ideas

  • golang dns client using RDAP with json output

Start a project

go mod init github.com/jelly/$project

Common modules

  • cobra
  • logrus

Types

  • slice []string{"lala", "lolol"};
  • string
  • bool

Gotchas

Go executes init functions automatically at program startup, after global variables have been initialized.

Type assertions

var greeting interface{} = "hello world"
greetingStr, ok := greeting.(string)
if !ok {
	fmt.Println("not asserted")
}

Type asertions can only take place on interfaces, on our first line we assign a string to the interface greeting. While greeting is a string now, the interface exposed to us is a string. To return the original type of greeting we can assert that it is a string using greeting.(string).

If you are not sure of the type of an interface a switch can be used:

var greeting interface{} = 42

switch g := greeting.(type) {
	case string:
		fmt.Println("string of length", len(g))
	case int:
		fmt.Println("integer of value", g)
	case default:
		fmt.Println("no idea what g is")
}

This all is called an assertion, as the original type of greeting (interface) is not changed.

Type conversions

var greeting := []byte("hello world")
greetingStr := string(greeting)

In Golang a type defines:

  1. How the variable is stored (underlying data structure)
  2. What you can do wit hthe variable (methods/ functions it can be used in)

In Golang one can define it's own type

// myInt is a new type who's base type is `int`
type myInt int

// The AddOne method works on `myInt` types, but not regular `int`s
func (i myInt) AddOne() myInt { return i + 1}

func main() {
	var i myInt = 4
	fmt.Println(i.AddOne())
}

As a myInt uses a similiar data structure underneath, we can convert a myInt to an int.

var i myInt = 4
originalInt := int(i)

This means, types can only be converted if the underlying data structure is the same.

declaring variables

There are two ways to declare variables in golang (Go infers the type from initiailization)

  1. using the var keyword var int foo = 4
  2. using a short declaration operator (:=) foo := 4

Differences:

var keyword:

  • used to declare and initialize the variables inside and outside of functions
  • the scope can therefore be package level or global level scope or local scope
  • declaration and initialization of the variables can be done separately
  • optionally can put a type with the decleration

short decleration operator:

  • used to declare and initialize the variable only inside the functions
  • variables has only local scope as they can only be declared in functions
  • decleration and initialization of the variables must be done at the same time
  • there is no need to put a type

struct

Named structs

type Employee struct {
	firstName string
	lastName string
	age int
}

func main() {
	emp1 := Employee{
		firstName: "Sam",
		lastName: "Anderson",
		age: 25,
	}
	// Zero value of a struct, all fields with be 0 or ""
	var emp2 Employee
}

Anonymous struct

foo := struct {
	firstName string,
	lastName string,
}{
	firstName: "Steve",
	lastName: "Jobs",
}

Pointers to a struct

emp1 := &Employee{
	firstName: "Steve",
	lastName: "Jobs",
}

fmt.Println("First Name:", (*emp1).firstName);
fmt.Println("First Name:", emp1.firstName);
Anonymous fields

It is possible to create structs with fields that contain only a type without the field name. Even thought they have no explicit name, by default the name of an anonymous field is the name of its type.

type Person struct {
	string
	int
}
Nested structs
type Address struct {  
    city  string
    state string
}

type Person struct {  
    name    string
    age     int
    address Address
}

Fields that belong to an anonymous struct field in a struct are called promoted fields since they can be accessed as if they belong to the struct which holds the anonymous struct field.

type Address struct {  
    city string
    state string
}
type Person struct {  
    name string
    age  int
    Address
}

func main() {  
    p := Person{
        name: "Naveen",
        age:  50,
        Address: Address{
            city:  "Chicago",
            state: "Illinois",
        },
    }

    fmt.Println("Name:", p.name)
    fmt.Println("Age:", p.age)
    fmt.Println("City:", p.city)   //city is promoted field
    fmt.Println("State:", p.state) //state is promoted field
}

Structs equality

Structs are value types and are comparable if each of their fields are comparable. Two struct variables are considered equal if their corresponding fields are equal.

Interface

An interface is a set of methods and a type.

For structs

An interface is a placeholder for a struct which implements it's functions, which can be used to allow a a method to take an interface as argument.

package main

import (
    "fmt"
    "math"
)

type geometry interface {
    area() float64
    perim() float64
}

type rect struct {
    width, height float64
}

func (r rect) area() float64 {
    return r.width * r.height
}
func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

type circle struct {
    radius float64
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}

func main() {
    r := rect{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)
    measure(c)
}

The interface{} type

The interface{} type, the empty interface has no methods. This means that any function which takes interface{} value as parameter, you can supply that function with any value.

package main

import (
    "fmt"
)

func checkType(i interface{}) {
    switch i.(type) {          // the switch uses the type of the interface
    case int:
        fmt.Println("Int")
    case string:
        fmt.Println("String")
    default:
        fmt.Println("Other")
    }
}

func main() {
    var i interface{} = "A string"

    checkType(i)   // String
}

Equality of interface values

An interface is equal if they are both nil or the underlying value and the type are equal.

package main
 
import (
    "fmt"
)
 
func isEqual(i interface{}, j interface{}) {
    if(i == j) {
        fmt.Println("Equal")
    } else {
        fmt.Println("Inequal")
    }
}
 
func main() {
    var i interface{}
    var j interface{}
     
    isEqual(i, j)   // Equal
     
    var a interface{} = "A string"
    var b interface{} = "A string"
     
    isEqual(a, b)   // Equal
}

goroutines

modules?

context