Slice pre-allocation
#tips #tricks
Did you know that itβs possible to use a pre-allocated slice without specifying the length of the array (zero). This allows us to use append just like we would:
π @gopher_academy | @GolangEngineers
#tips #tricks
Did you know that itβs possible to use a pre-allocated slice without specifying the length of the array (zero). This allows us to use append just like we would:
// instead ofββββββββ
a := make([]int, 10)
a[0] = 1
// use this
b := make([]int, 0, 10)
b = append(b, 1)
π @gopher_academy | @GolangEngineers
π₯1π1πΎ1π1
Chaining technique
#tips #tricks
The technique of chaining can be applied to function (pointer) receivers. To illustrate this, letβs consider a Person struct with two functions, AddAge and Rename, that can be used to modify it.
π @gopher_academy | @GolangEngineers
#tips #tricks
The technique of chaining can be applied to function (pointer) receivers. To illustrate this, letβs consider a Person struct with two functions, AddAge and Rename, that can be used to modify it.
type Person struct {If youβre looking to add age to a person and then rename them, the typical approach is as follows:
Name string
Age int
}
func (p *Person) AddAge() {
p.Age++
}
func (p *Person) Rename(name string) {
p.Name = name
}
func main() {Alternatively, we can modify the AddAge and Rename function receivers to return the modified object itself, even if they donβt typically return anything.
p := Person{Name: "Aiden", Age: 30}
p.AddAge()
p.Rename("Aiden 2")
}
func (p *Person) AddAge() *Person {By returning the modified object itself, we can easily chain multiple function receivers together without having to add unnecessary lines of code:
p.Age++
return p
}
func (p *Person) Rename(name string) *Person {
p.Name = name
return p
}
p = p.AddAge().Rename("Aiden 2")ββββββββ
π @gopher_academy | @GolangEngineers
π8π₯2πΎ2
Using import with β_β for package initialization
#tips #tricks
Sometimes, in libraries, you may come across import statements that combine an underscore (_) like this:
Letβs consider an example to better understand how it works:
π @gopher_academy | @GolangEngineers
#tips #tricks
Sometimes, in libraries, you may come across import statements that combine an underscore (_) like this:
import ( _ "google.golang.org/genproto/googleapis/api/annotations" )This will execute the initialization code (init function) of the package, without creating a name reference for it. This allows you to initialize packages, register connections, and perform other tasks before running the code.
Letβs consider an example to better understand how it works:
// underscoreββββββββ
package underscore
func init() {
fmt.Println("init called from underscore package")
}
// mainpackage main
import (
_ "lab/underscore"
)
func main() {}
// log: init called from underscore package
π @gopher_academy | @GolangEngineers
π5πΎ2π₯1
Use import with dot .
#tips #tricks
Having explored how we can use import with underscore, letβs now look at how the dot . operator is more commonly used.
As a developer, the dot . operator can be used to make the exported identifiers of an imported package available without having to specify the package name, which can be a helpful shortcut for lazy developers.
Pretty cool, right? This is especially useful when dealing with long package names in our projects, such as
To demonstrate, hereβs a brief example:
π @gopher_academy | @GolangEngineers
#tips #tricks
Having explored how we can use import with underscore, letβs now look at how the dot . operator is more commonly used.
As a developer, the dot . operator can be used to make the exported identifiers of an imported package available without having to specify the package name, which can be a helpful shortcut for lazy developers.
Pretty cool, right? This is especially useful when dealing with long package names in our projects, such as
externalmodel
or doingsomethinglonglib
To demonstrate, hereβs a brief example:
package mainββββββββ
import (
"fmt"
. "math"
)
func main() {
fmt.Println(Pi) // 3.141592653589793
fmt.Println(Sin(Pi / 2)) // 1
}
π @gopher_academy | @GolangEngineers
π8πΎ2π1
Ternary with generic
#tips #tricks
Go does not have built-in support for ternary operators like many other programming languages:
ββββββββ
π @gopher_academy | @GolangEngineers
#tips #tricks
Go does not have built-in support for ternary operators like many other programming languages:
# pythonWith Goβs generics in version 1.18, we now have the ability to create a utility that allows for ternary-like functionality in just a single line of code:
min = a if a < b else b
# c#
min = x < y ? x : y
// our utility
func Ter[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
func main() {
fmt.Println(Ter(true, 1, 2)) // 1
fmt.Println(Ter(false, 1, 2)) // 2
}
ββββββββ
π @gopher_academy | @GolangEngineers
π8πΎ3
Synchronize a map
#tips #tricks
To synchronize a map, you will see code that uses a
You gain some benefits using a
ββββββββ
π @gopher_academy | @GolangEngineers
#tips #tricks
To synchronize a map, you will see code that uses a
sync.Mutex
or sync.RWMutex
.You gain some benefits using a
sync.RWMutex
with read-heavy operations on the map.var (Instead of using a map mutex pair, you can use sync.Map:
s map[int]bool
m sync.RWMutex
)
for i := 0; i < 7; i++ {
go func(i int) {
m.Lock()
defer m.Unlock()
s[i] = true
}(i)
}
// Elsewhere
m.RLock()
for k, v := range s {
fmt.Println(k, v)
}
m.RUnlock()
var s sync.MapIf you feel uneasy about the use of any in all
for i := 0; i < 7; i++ {
go func(i int) {
s.Store(i, true)
}(i)
}
// Elsewhere
s.Range(func(k, v any) bool {
fmt.Println(k.(int), v.(bool))
return true
})
sync.Map
functions, you could define a generic wrapper:type Map[K any, V any] struct {And then use the wrapper instead:
m sync.Map
}
func (m *Map[K, V]) Load(key K) (V, bool) {
v, ok := m.m.Load(key)
return v.(V), ok
}
func (m *Map[K, V]) Range(fn func(key K, value V) bool) {
m.m.Range(func(key, value any) bool {
return fn(key.(K), value.(V))
})
}
func (m *Map[K, V]) Store(key K, value V) {
m.m.Store(key, value)
}
var s Map[int, bool]One caveat is that the Range function is different from holding a lock around the range loop in the
for i := 0; i < 7; i++ {
go func(i int) {
s.Store(i, true)
}(i)
}
// Elsewhere
s.Range(func(k int, v bool) bool {
fmt.Println(k, v)
return true
})
sync.RWMutex
example. Range does not necessarily correspond to any consistent snapshot of the mapβs contents.ββββββββ
π @gopher_academy | @GolangEngineers
β€6π2