Sitemap
Devpriya Shivani

Devpriya Shivani

Go Function Idioms

4 min readJul 6, 2024

--

Press enter or click to view image in full size
Function Idioms in Go

Often praised for its simplicity and efficiency, Go offers a rich set of idioms that enhance function usage. These idioms can help make your code more readable, maintainable, and idiomatic. In this post, we’ll explore some of the most commonly used function idioms in Go.

1. Multiple Return Values

One of the standout features of Go functions is their ability to return multiple values. This is particularly useful for error handling and returning additional information without the need for complex data structures.

package main

import "fmt"

func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}

func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}

Output:

Result: 5

Try it yourself!

Pass 0 instead of 2 in divide function.

You can return multiple values but in standard library, not more than 12 functions are returning more than 2 values. There are some which returns 3, none return more than this.

2. Named Return Values

Named return values can improve code clarity by providing a clear understanding of what each return value represents. They can also simplify the function implementation by eliminating the need to explicitly return values at the end of the function.

package main

import "fmt"

func rectangleProps(length, width float64) (area, perimeter float64) {
area = length * width
perimeter = 2 * (length + width)
return
}

func main() {
area, perimeter := rectangleProps(10, 5)
fmt.Println("Area:", area)
fmt.Println("Perimeter:", perimeter)
}

Output:

Area: 50
Perimeter: 30

3. Defer

The defer statement is used to ensure that a function call is performed later in a program's execution, usually for cleanup. Deferred functions are executed in LIFO order, meaning the last deferred function is executed first.

package main

import "fmt"

func main() {
fmt.Println("Starting the program")
defer fmt.Println("Deferred call 1")
defer fmt.Println("Deferred call 2")
fmt.Println("Ending the program")
}

Output:

Starting the program
Ending the program
Deferred call 2
Deferred call 1

4. Variadic Functions

Variadic functions can take a variable number of arguments. The ... syntax allows you to pass any number of arguments of the specified type to the function.

package main

import "fmt"

func sum(numbers ...int) int {
total := 0
for _, number := range numbers {
total += number
}
return total
}

func main() {
fmt.Println(sum(1, 2, 3, 4, 5)) // Output: 15
}

Output:

Get Devpriya Shivani’s stories in your inbox

Join Medium for free to get updates from this writer.

15

5. Closure

Go supports anonymous functions, which can form closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables.

package main

import "fmt"

func main() {
var number int
func() {
fmt.Println("Direct Closure: ", number)
}()

function := func() {
fmt.Println("Variable Closure: ", number)
}
function()
}

Output:

Direct Closure:  0
Variable Closure: 0

Use Cases for Closures

  1. Stateful Functions: Closures can be used to create functions that maintain state across invocations without using global variables.
  2. Encapsulation: They help in encapsulating data and functions together, leading to better modular code.
  3. Callbacks and Event Handlers: Closures are useful in scenarios requiring callbacks or event handling where the function needs to access some context or state.

6. Methods

In Go, you can define methods on types. This is a powerful feature that allows you to define behavior associated with a particular type, making your code more object-oriented.

package main

import "fmt"

type Rectangle struct {
length, width float64
}

func (rect Rectangle) area() float64 {
return rect.length * rect.width
}

func (rect Rectangle) perimeter() float64 {
return 2 * (rect.length + rect.width)
}

func main() {
rectangle := Rectangle{length: 10, width: 5}
fmt.Println("Area:", rectangle.area())
fmt.Println("Perimeter:", rectangle.perimeter())
}

Output:

Area: 50
Perimeter: 30

7. Function Types

Functions in Go are first-class citizens, which means they can be assigned to variables, passed as arguments, and returned from other functions.

package main

import "fmt"

type operation func(a, b int) int

func add(a, b int) int {
return a + b
}

func main() {
var op operation
op = add
fmt.Println(op(10, 20)) // Output: 30
}

Output:

30

Some other Best Practices for Go

  • Reduce Else Blocks: Try to reduce the use of else blocks. Use if for returning negative statements.
  • Use Switch: Use switch in cases where else seems necessary.
  • Nil as Zero Value Literal: When returning an error message, return zero value literal for other types. It improves the readability of the code.

Conclusion

These idioms are just a few of the many ways Go enables developers to write clear, concise, and efficient code. Understanding and utilizing these idioms can greatly enhance your Go programming experience and help you write more idiomatic Go code. As you continue to work with Go, you’ll discover even more patterns and practices that can make your code even more powerful and expressive.

--

--