12 Personal Go Tricks That Transformed My Productivity

Photograph by Lucas Santos onΒ Unsplash



Writer

I sometimes share insights on System Design & Go at Devtrovert. Be at liberty to take a look at my LinkedIn Phuong Le for the most recent posts.


Whereas engaged on manufacturing initiatives, I seen that I used to be often duplicating code and using sure strategies with out realizing it till later when reviewing my work.

To handle this challenge, I developed an answer that has confirmed to be fairly useful for me, and I assumed it is perhaps helpful for others as effectively.

Beneath are some helpful and versatile code snippets randomly picked from my utilities library, with none specific categorization or system-specific methods.



1. Time elapsed trick

In the event you’re concerned with monitoring the execution time of a perform in Go, there’s a easy and environment friendly trick you should utilize with only a single line of code utilizing the β€œdefer” key phrase. All you want is a TrackTime perform:

// Utility
func TrackTime(pre time.Time) time.Period {
  elapsed := time.Since(pre)
  fmt.Println("elapsed:", elapsed)

  return elapsed
}

func TestTrackTime(t *testing.T) {
  defer TrackTime(time.Now()) // <--- THIS

  time.Sleep(500 * time.Millisecond)
}

// elapsed: 501.11125ms
Enter fullscreen mode

Exit fullscreen mode



2. Slice pre-allocation

As per the insights shared within the article β€œGo Performance Boosters”, pre-allocating a slice or map can considerably improve the efficiency of our Go packages.

Nonetheless, it’s price noting that this strategy can typically end in bugs if we inadvertently use β€œappend” as a substitute of indexing, like a[i].

Do you know that it’s attainable to make use of a pre-allocated slice with out specifying the size of the array (zero), as defined within the aforementioned article? This enables us to make use of append similar to we might:

// as a substitute of
a := make([]int, 10)
a[0] = 1

// use this
b := make([]int, 0, 10)
b = append(b, 1)
Enter fullscreen mode

Exit fullscreen mode



3. Chaining

The strategy of chaining may be utilized to perform (pointer) receivers. As an example this, let’s contemplate a Particular person struct with two features, AddAge and Rename, that can be utilized to switch it.

kind Particular person struct {
  Identify string
  Age  int
}

func (p *Particular person) AddAge() {
  p.Age++
}

func (p *Particular person) Rename(title string) {
  p.Identify = title
}
Enter fullscreen mode

Exit fullscreen mode

In the event you’re wanting so as to add age to an individual after which rename them, the everyday strategy is as follows:

func principal() {
  p := Particular person{Identify: "Aiden", Age: 30}

  p.AddAge()
  p.Rename("Aiden 2")
}
Enter fullscreen mode

Exit fullscreen mode

Alternatively, we are able to modify the AddAge and Rename perform receivers to return the modified object itself, even when they don’t sometimes return something.

func (p *Particular person) AddAge() *Particular person {
  p.Age++
  return p
}

func (p *Particular person) Rename(title string) *Particular person {
  p.Identify = title
  return p
}
Enter fullscreen mode

Exit fullscreen mode

By returning the modified object itself, we are able to simply chain a number of perform receivers collectively with out having so as to add pointless strains of code:

p = p.AddAge().Rename("Aiden 2")
Enter fullscreen mode

Exit fullscreen mode



4. Go 1.20 permits parsing of slices into arrays or array pointers

When we have to convert a slice right into a fixed-size array, we are able to’t assign it immediately like this:

a := []int{0, 1, 2, 3, 4, 5}
var b[3]int = a[0:3]

// can't use a[0:3] (worth of kind []int) as [3]int worth in variable 
// declaration compiler(IncompatibleAssign)
Enter fullscreen mode

Exit fullscreen mode

With a purpose to convert a slice into an array, the Go group up to date this function in Go 1.17. And with the discharge of Go 1.20, the conversion course of has grow to be even simpler with extra handy literals:

// go 1.20
func Take a look at(t *testing.T) {
    a := []int{0, 1, 2, 3, 4, 5}
    b := [3]int(a[0:3])

  fmt.Println(b) // [0 1 2]
}

// go 1.17
func TestM2e(t *testing.T) {
  a := []int{0, 1, 2, 3, 4, 5}
  b := *(*[3]int)(a[0:3])

  fmt.Println(b) // [0 1 2]
}
Enter fullscreen mode

Exit fullscreen mode

Only a fast notice: you should utilize a[:3] as a substitute of a[0:3]. I’m mentioning this for the sake of readability.



5. Utilizing import with β€˜_’ for bundle initialization

Typically, in libraries, chances are you’ll come throughout import statements that mix an underscore (_) like this:

import (
  _ "google.golang.org/genproto/googleapis/api/annotations" 
)
Enter fullscreen mode

Exit fullscreen mode

This may execute the initialization code (init perform) of the bundle, with out creating a reputation reference for it. This lets you initialize packages, register connections, and carry out different duties earlier than operating the code.

Let’s contemplate an instance to raised perceive the way it works:

// underscore
bundle underscore

func init() {
  fmt.Println("init known as from underscore bundle")
}
// mainpackage principal 
import (
  _ "lab/underscore"
)
func principal() {}
// log: init known as from underscore bundle
Enter fullscreen mode

Exit fullscreen mode



6. Use import with dot .

Having explored how we are able to use import with underscore, let’s now take a look at how the dot . operator is extra generally used.

As a developer, the dot . operator can be utilized to make the exported identifiers of an imported bundle obtainable with out having to specify the bundle title, which is usually a useful shortcut for lazy builders.

Fairly cool, proper? That is particularly helpful when coping with lengthy bundle names in our initiatives, similar to β€˜externalmodel’ or β€˜doingsomethinglonglib’

To reveal, right here’s a short instance:

bundle principal

import (
  "fmt"
  . "math"
)

func principal() {
  fmt.Println(Pi) // 3.141592653589793
  fmt.Println(Sin(Pi / 2)) // 1
}
Enter fullscreen mode

Exit fullscreen mode



7. A number of errors can now be wrapped right into a single error with Go 1.20

Go 1.20 introduces new options to the error bundle, together with assist for a number of errors and adjustments to errors.Is and errors.As.

One new perform added to errors is Be a part of, which we’ll take a more in-depth take a look at beneath:

var (
  err1 = errors.New("Error 1st")
  err2 = errors.New("Error 2nd")
)

func principal() {
  err := err1
  err = errors.Be a part of(err, err2)

  fmt.Println(errors.Is(err, err1)) // true
  fmt.Println(errors.Is(err, err2)) // true
}
Enter fullscreen mode

Exit fullscreen mode

If in case you have a number of duties that contribute errors to a container, you should utilize the Be a part of perform as a substitute of manually managing the array your self. This simplifies the error dealing with course of.



8. Trick to Test Interface at Compile Time

Suppose you have got an interface known as Buffer that comprises a Write() perform. Moreover, you have got a struct named StringBuffer which implements this interface.

Nonetheless, what for those who make a typo mistake and write Writeee() as a substitute of Write()?

kind Buffer interface {
  Write(p []byte) (n int, err error)
}

kind StringBuffer struct{}

func (s *StringBuffer) Writeee(p []byte) (n int, err error) {
  return 0, nil
}
Enter fullscreen mode

Exit fullscreen mode

You might be unable to examine whether or not StringBuffer has correctly carried out the Buffer interface till runtime. Nonetheless, through the use of this trick, the compiler will warn you through an IDE error message:

var _ Buffer = (*StringBuffer)(nil)

// can't use (*StringBuffer)(nil) (worth of kind *StringBuffer) 
// as Buffer worth in variable declaration: *StringBuffer 
// doesn't implement Buffer (lacking technique Write)
Enter fullscreen mode

Exit fullscreen mode



9. Ternary with generic

Go doesn’t have built-in assist for ternary operators like many different programming languages:

# python 
min = a if a < b else b
Enter fullscreen mode

Exit fullscreen mode

// c#
min = x < y ? x : y
Enter fullscreen mode

Exit fullscreen mode

With Go’s generics in model 1.18, we now have the power to create a utility that permits for ternary-like performance in only a single line of code:

// our utility
func Ter[T any](cond bool, a, b T) T {
  if cond {
    return a
  }

  return b
}

func principal() {
  fmt.Println(Ter(true, 1, 2)) // 1 
  fmt.Println(Ter(false, 1, 2)) // 2
}
Enter fullscreen mode

Exit fullscreen mode



10. Keep away from Bare Parameter

When coping with a perform that has a number of arguments, it may be complicated to grasp the that means of every parameter by simply studying its utilization. Contemplate the next instance:

printInfo("foo", true, true)
Enter fullscreen mode

Exit fullscreen mode

What do the primary β€˜true’ and the second β€˜true’ imply for those who don’t examine the printInfo? When you have got a perform with a number of arguments, it may be complicated to grasp the parameter that means.

Nonetheless, we are able to use feedback to make the code extra readable. For instance:

// func printInfo(title string, isLocal, carried out bool)

printInfo("foo", true /* isLocal */, true /* carried out */)
Enter fullscreen mode

Exit fullscreen mode

Some IDEs additionally assist this function by exhibiting feedback in perform name solutions, however it might have to be enabled in settings.



11. Methods to confirm if an interface is really nil

Even when an interface holds a worth of nil, it doesn’t essentially imply that the interface itself is nil. This could result in surprising errors in Go packages. So, it’s necessary to know methods to examine if an interface is definitely nil or not.

func principal() {
  var x interface{}
  var y *int = nil
  x = y

  if x != nil {
    fmt.Println("x != nil") // <-- precise
  } else {
    fmt.Println("x == nil")
  }

  fmt.Println(x)
}

// x != nil
// <nil>
Enter fullscreen mode

Exit fullscreen mode

In case you are not acquainted with this idea, I like to recommend that you just consult with my article about Go’s secrets and techniques relating to Interface{}: Nil is not Nil.

How can we decide whether or not an interface{} worth is nil? Thankfully, there’s a easy utility that may assist us obtain this:

func IsNil(x interface{}) bool {
  if x == nil {
    return true
  }

  return mirror.ValueOf(x).IsNil()
}
Enter fullscreen mode

Exit fullscreen mode



12. Unmarshal time.Period in JSON

When parsing JSON, utilizing time.Period is usually a cumbersome course of because it requires including 9 zeroes trailing of 1 second (i.e., 1000000000). To simplify this course of, I created a brand new kind known as Period:

kind Period time.Period
Enter fullscreen mode

Exit fullscreen mode

To allow parsing of strings like 1s or 20h5m into int64 durations, I additionally carried out a customized unmarshal logic for this new kind:

func (d *Period) UnmarshalJSON(b []byte) error {
  var s string
  if err := json.Unmarshal(b, &s); err != nil {
    return err
  }
  dur, err := time.ParseDuration(s)
  if err != nil {
    return err
  }
  *d = Period(dur)
  return nil
}
Enter fullscreen mode

Exit fullscreen mode

Nonetheless, you will need to notice that the variable β€˜d’ shouldn’t be nil as it might result in marshaling errors. Alternatively, you may also embrace a examine for β€˜d’ initially of the perform.”


I didn’t need to make the publish too lengthy and troublesome to comply with since these methods don’t depend upon any particular subject and canopy numerous classes.

In the event you discovered these methods helpful or have any insights of your personal to share, please be happy to go away a remark. I worth your suggestions and could be completely satisfied to love or advocate your concepts in response to this publish.

Pleased tricking!

source