Functions

Function arguments of same type can be separated by comma and declare type only in the end

// Both greeting and name variables are same string type, so no need to define type for each of them
func sayGreeting(greeting, name string) {
    fmt.Println(greeting, name)
}

Example of function with pointer arguments. Note, slice and maps always passed by reference even if there is no pointer operators.

// Output:
// Hello John
// James
// James
func main() {
    greeting := "Hello"
    name := "John"
    sayGreeting(&greeting, &name)
    fmt.Println(name)

    s := []int{1,2,3}
    changeSlice(s)
    // output: [0,2,3]
    fmt.Println(s)
}

func sayGreeting(greeting, name *string) {
    fmt.Println(*greeting, *name)
    *name = "James"
    fmt.Println(*name)
}

func changeSlice(slice []int) {
    slice[0] = 0
}

Functions can accept variadic parameters. They must be placed as last and only once. Parameters will be converted to slice.

func main() {
    sum("The sum is:", 1, 2, 3, 4)
}
// values is variadic parameter and it is slice of integers
func sum(msg string, values ...int) {
    result := 0
    for _, v := range values {
        result += v
    }
    // Output: The sum is: 10
    fmt.Println(msg, result)
}

Function can return pointer instead of value

func main() {
    s := sum(1, 2, 3, 4)
    fmt.Println("The sum is:", *s)
}
func sum(values ...int) *int {
    result := 0
    for _, v := range values {
        result += v
    }

    return &result
}

Function can define mean return value:

func main() {
    s := sum(1, 2, 3, 4)
    fmt.Println("The sum is:", s)
}
func sum(values ...int) (result int) {
    for _, v := range values {
        result += v
    }
    // this is syntax sugar so interpreter will just return result variable
    return
}

Function can return multiple values:

func main() {
    d, err := divide(5.0, 0.0)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(d)
}

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

    return a / b, nil
}

Function is type so it can be declared as variables, can be passed as parameters and can be return result of another function

func main() {
    // Here we define variable as simple anonymous function without parameters and return values
    var simplePrint func()
    simplePrint = func() {
        fmt.Println("Hello world")
    }
    // Output: Hello world
    simplePrint()

    // Here we define variable that has type of function with 2 float64 parameter and float64, error return values
    var divide func(float64, float64) (float64, error)
    divide = func(a, b float64) (float64, error) {
        if b == 0 {
            return 0.0, fmt.Errorf("Cannot divide by zero")
        }

        return a / b, nil
    }
    d, err := divide(1.0, 0.0)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(d)
}

Types can contain methods. Then receiver will be defined in parentheses before function name

func main() {
    g := greeter{
        greeting: "Hello",
        name:     "John",
    }
    g.greet()

    var c counter
    c = 0
    c.increment()
    // output: 1
    fmt.Println(c)
}

type greeter struct {
    greeting string
    name     string
}
// By default receiver for the method(g greeter in this case) is passing by value. To pass by reference need to use pointer operator
func (g greeter) greet() {
    fmt.Println(g.greeting, g.name)
}

type counter int

func (c *counter) increment() {
    *c++
}