Functions

Syntax of function signature

func funcName(arg1 dataType, arg2 dataType) returnType

In a case where there are more than one return type:

func funcName(arg1 dataType, arg2 dataType) (returnType1, returnType2)

Example function:

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

Since all the arguments in add function are in the same data type int, we can also write the function as below. As you can see we have given the data type of the arguments only once.

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

Return value

Functions in Go can return,

  • No value
  • Single value
  • Multiple values An example per each type of the above functions is given below.
func printDiscount(price float32) {
    // returns no value
    fmt.Printf("Discounted value: .2%f", price*0.8)
}

func increment(x int) int {
    // returns a single value of type int
    x++
    return x
}

func getPass(marks int) (string, string) {
    // returns multiple values with type string
    // also note that the return values can be of different data types
    status := "Fail"
    level := "F"
    if marks >= 75 {
        status = "Pass"
        level = "A"
    } else if marks >= 55 {
        status = "Pass"
        level = "B"
    }

    return status, level
}

Go doesn't allow you to have unused variables. So, it has a way to ignore return values. Assume there is a function that returns two values, but you only want the first value it returns.

func getCoordinates() (int, int) {
    x := 3
    y := 4
    return x, y
}

If you only want x coordinate from the above function, use the below syntax.

x, _ := getCoordinates()

The _ tells the Go to ignore the second return value.

In Go, we can also name return values. In a scenario like that, the function execution is a bit different. Check out the function given below.

func getCoords() (x, y int) {
    // ... some code here
    return
}

As you can see we have named two return values as x and y, and given them the type int. With that, they get initialized with the value 0. As you can see we have simply used return. There the function returns x and y in order. You need not to explicitly say return x, y.

So when to use named return values? Think you have a function where you need to return the height, width, and length of an object. In such a case it may look more readable to state (height, width, length int) instead of (int, int, int).

Guard Clause

Guard clauses mean early returns. Check the func getPass(marks int) (string, string) that I have written above. There we have a single reutrn right before the end of the function. But we can have early returns per each condition, which is ideal. It provides a linear approach to logic trees.

func getPass(marks int) (string, string) {
    if marks >= 75 {
        return "Pass", "A"
    } else if marks >= 55 {
        return "Pass", "B"
    } 
    return "Fail", "F"
}

Pass by value

In Go, variables are passed by value, not by reference.

func main() {
    x := 10
    increment(x)
    fmt.Printf("incremented value: %d", x)
}

func increment(x int) {
    x++
}

In the above code, the main function gives "incremented value: 10".
So why is it not incremented even though we have called increment() function? That is because we are not returning the incremented value and assigning it to x. In the above code, a new memory address is used in increment(). The correct way is given below.

func main() {
    x := 10
    x = increment(x)
    fmt.Printf("incremented value: %d", x)
}

func increment(x int) int {
     x++
     return x
}