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:
- How the variable is stored (underlying data structure)
- 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)
- using the var keyword
var int foo = 4
- 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
}
Promoted Fields
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
}