5 things before you venture into your maiden Golang Project
Somethings are so simple that we sometime overlook them and it starts right from there.
What’s in a name of a variable?
The above link subtly covers why we keep single letter variable names in golang.
These things often ease your conversations in the code pairing or code reviewing times.
What is the difference between Pointer receiver and Value receiver?
Use Pointer functions when you plan to change the state of struct. In the below example we see mood of Person gets changed, hence we should be using the pointer receiver method.
Pointer receiver, passes the reference to same struct.
type Person struct {
mood string
}
func (p *Person) salaryCredited() {
p.mood = "Happy"
}
func (p *Person) print() {
fmt.Println(p.mood)
}
Use Value receiver if you do not plan to change the struct state. In the below example the state of struct does not change and hence we are don’t need to associate a pointer function to struct.
Value receiver, passes the copy of the struct.
type Rectangle struct {
length int
breadth int
}
func (r Rectangle) area() int {
return 2 *(r.length +r.breadth)
}
If in doubt, use a pointer.
You might often hear this in this part of world, Don’t fall for it, unless you diligently profile your every code. Every pointer receiver, gets a place in heap, which means there is an overhead on garbage collector to free the memory. So use value receiver wherever possible, unless you have to copy a large struct or need semantics of pointers.
Additionally you can do an escape analysis by running this command, to check what goes into your heap.
go build -gcflags="-m"
What’s an Interface ?
If you are coming from Java background first erase the definition of interface and start afresh.
Interfaces are those open functions which stick to their implementers.
Terminology in golang for this is structural typing.
You see a struct implementing all methods of an interface, then the interface is automatically stuck to this struct. Nothing else needed
type EarthLover interface {
run()
}
type WaterLover interface {
swim()
}
No keywords needed to be associated with EarthLover or Waterlover interface. All you have to do is have structs implementing the method. In the below example Cat become EarthLover, Whale becomes WaterLover and our croc is both EarthLover and WaterLover as it implements both run and swim function.
type Cat struct{}
func (Cat) run() {}
type Whale struct{}
func (Whale) swim() {}type Crocodile struct{}
func (Crocodile) swim() {}
func (Crocodile) run() {}
Does ordering of variables in a struct matter?
Yes, it does. Can you guess what the output will be for below snippet?
For reference , bool takes 1 byte, float64 takes 8 bytes and int32 takes 4 bytes.
func main() {
a := myStruct{}
o := optimizedMyStruct{}
fmt.Println(unsafe.Sizeof(a)) //24
fmt.Println(unsafe.Sizeof(o)) //16
}
type myStruct struct {
myBool bool
myFloat float64
myInt int32
}
type optimizedMyStruct struct {
myFloat float64
myBool bool
myInt int32
}
And this is the output.
24
16
The important aspect here to understand is the way memory allocation happens in golang. The memory is always allocated in blocks of 8.
So in the first struct, this is how the memory is allocated
And this is how the memory allocation for optimized struct is done
So it’s always important to order struct variables in a way that it fits optimally in allocated memory blocks.
References:
P.S For folks wondering where is the 5th one.