Functions

A function declaration in Go :

type dog struc { }

func (d dog) bark(a string) (x,y string) { return "wooh 1","wooh 2" }

| 1 |   2   | 3  |    4    |      5     |         6                  |
  1. The keyword func is used to declare a function.
  2. A function can optionally be bound to a specific type. This is called the receiver. A function with a receiver is a method.
  3. Name of the function.
  4. Input parameter. They are passed by value (they are copied).
  5. Return parameters and types of the function. Functions in Go can have multiple return values.
  6. Function's body.

Some functions properties :

  • Syntax

     func do()
     {                       // invalid syntax
         ...
     }
    
     func do() {             // valid syntax
         ...
     }
    
  • main() function , the entry point of the program.

     package main
    
     import "fmt"
    
     func main() {
         fmt.Println("Hello, World !")
     }
    
  • Function overloading is not allow in Go. Functions cannot have same name with different argument list. The main reason is that overloading functions forces the runtime to do additional type matching which reduces performance; no overloading means no function dispatch is needed.

     package main
    
     import "fmt"
    
     func eat(){}
    
     func eat(food string) {}
    
     func main() {
         fmt.Println("Hello, playground")
     }
    
     [output]
     prog.go:8: eat redeclared in this block
     previous declaration at prog.go:5
    [process exited with non-zero status]
    
  • Multiple return values

     package main
    
     import "fmt"
    
     func multi()(x,y string){           // x , y are return value , declare on the right.
         return "x","y"                  // return multiple values.
     }
     func main() {
         a,b := multi()                  // received and assigned multiple values.
         fmt.Println(a,b)
         fmt.Println(multi())
     }
    
     [output]
     x y
     x y
    
  • Blank identifier. The blank identifier "_" can be used to discard values, effectively assigning the right-hand-side value to nothing.

     package main
    
     import "fmt"
    
     func main() {
         x,_ := do()
         fmt.Println(do())
         fmt.Println(x)
     }
    
     func do() (int, int) {
         return 5, 6
     }
    
     [output]
     5 6
     5
    
  • Empty Return (Named return variables)

     package main
    
     import "fmt"
    
     func emp_return(i int)(x,y int){
         x = i + 1
         y = i + 2
         return
     }
    
     func main() {
         fmt.Println(emp_return(3))
     }
    
     [output]
     4 5
    
  • Defer keyword ( Please remind me to close the door before leaving ! ). Defer is used to ensure that a function call is performed later in a program execution, usually for purposes of cleanup.

     package main
    
     import "fmt"
    
     func connectDB () {                         // Open call
         fmt.Println( ".. DB <connected>" )
     }
    
     func disconnectDB () {
         fmt.Println( ".. DB <disconnected>" )   // Close call
     }
    
     func main() {
         connectDB()
         defer disconnectDB()
    
         fmt.Println(".... <doing some DB operations>")
         fmt.Println(".... Oops! crash or network error")
         return                                           //terminate the program
    
         // deferred function executed here just before actually returning, 
         // even if there is a return or abnormal termination before
     }
    
     [output]
     .. DB <connected>
     .... <doing some DB operations>
     .... Oops! crash or network error
     .. DB <disconnected>
    

    This has 3 advantages:

    (1) it keeps the Close call near the Open call for easy understanding.

    (2) if the function had multiple return statements (perhaps one in an if and one in an else) Close call will happen before both of them.

    (3) deferred functions will still run even if a run-time panic occurs.

    If we use multiple defers in a program , it behaves like a stack: the functions are execute in Last In First Out (LIFO) order.

     package main
    
     import "fmt"
    
     func A() {
         fmt.Println( ".. A" )
     }
    
     func B() {
         fmt.Println( ".. B" )
     }
    
     func main() {
          defer A()
          defer B()
    
          //this function ends here
          //deferred functions executed in LIFO (last in first out) order
     }
    
     [output]
     .. B
     .. A
    
  • Panic & Recover.

    Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally (now , only defer have the ability to stop the crash), and then F returns to its caller. The process continues up the stack , like dominos falling down until all functions in the current goroutine have returned, at which point the program crashes.

    Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

      package main
    
      import "fmt"
    
      func main() {
          panic("PANIC")              // stop here
          r := recover()
          fmt.Println(r)
      }
    
      [output]
      panic: PANIC
    
      goroutine 1 [running]:
      main.main()
      /tmp/sandbox938942131/main.go:6 +0xc0
      ...
    

    the call to recover will never happen in this case because the call to panic immediately stops execution of the function. Instead we have to pair it with defer:

      package main
    
      import "fmt"
    
      func main() {
          defer func() {    
              r := recover()
              fmt.Println(r)
          }()
          panic("PANIC")
      }
    
      [output]
      PANIC
    

    Further Reference :

  • Defer, Panic, and Recover

  • Variadic Functions (...). Variadic Function is a function that will take an arbitrary number of ints as arguments.

      package main
    
       import "fmt"
    
      func add(args ...int) int {                 // "...int" here is a type []int
          total := 0
          for i := 0 ; i < len(args) ; i++ {
              total += args[i]
          }
          return total
      }
    
      func main() {
          fmt.Println(add(1,2,3))
      }
    
      [output]
      6
    

    By using ... before the type name of the last parameter you can indicate that it takes zero or more of those parameters. In this case we take zero or more ints. We invoke the function like any other function except we can pass as many ints as we want.

    For example, fmt.Println is a common variadic function,

      func Println(a ...interface{}) (n int, err error)
    

    If you already have multiple args in a slice, apply them to a variadic function using func(slice...) like this.

  • Anonymous Function. Go supports anonymous functions, which can form closures. Anonymous functions are useful when you want to define a function inline without having to name it.

    a anonymous function cannot stand on its own , it will cause the compiler error : non-declaration statement outside function body, but it can be assigned to a variable :

      package main
    
      import "fmt"
    
      func main() {
          add := func(x, y int) int {
              return x + y
             }
          fmt.Println(add)        // add is a local variable that has the type "func(int, int) int"
                                  // add is an address [0x20200]
          fmt.Println(add(1,3))   // do this if you wanna use it.
      }
    
      [output]
      0x20200
      4
    

    Anonymous Function can be used in the following way also (see section net/http - hello2.go for more details),

      package main
    
      import "fmt"
    
      func hello(w string, r string) {
          fmt.Println(w,r)
      }
    
      func HandleFunc(handler func(w string, r string)) {
          fmt.Println("handler address :", handler)
          handler("ResponseWriter","*Request")
      }
    
      func main() {
          HandleFunc(hello)                   // this is interesting ! only the function name and
      }                                       // same signature (w string, r string) is required !!
    
      [output]
      handler address : 0x401000
      ResponseWriter *Request
    
  • Closure. Golang can pass functions to other functions. Function in Go are first class citizens (which means you can pass it arround like values).

      package main
    
      import "fmt"
    
      func auto_increment() func() int {
          i := 0
          return func() int {
              i += 1
              return i
          }
      }
    
      func main() {
    
          x := auto_increment()
    
          fmt.Println(x())            // do x() if you wanna use function x.
                                      // x() will modified local variable i to 1 , and return i
          fmt.Println(x())            // x() will modified local variable i to 2 , and return 2
          fmt.Println(x())            // x() will modified local variable i to 3 , and return 3
    
          y := auto_increment()
          fmt.Println(y())            // new function , new internal local variable i
      }
    

    This function auto_increment() returns another function, which we define anonymously in the body of auto_increment(). The returned function closes over the variable i to form a closure.

    Further Ref:

    1. Why Go’s Closure Can Be Dangerous
  • Recursion Function. Go supports recursive functions. Here is a classic factorial example.

      package main
    
      import "fmt"
    
      func factorial(n int) int {
          if n == 0 {
              return 1
          }
          return n * factorial(n-1)
      }
      func main() {
          fmt.Println(factorial(9))
      }
    
      [output]
      362880
    

    Closure and recursion are powerful programming techniques which form the basis of a paradigm known as functional programming. Most people will find functional programming more difficult to understand than an approach based on for loops, if statements, variables and simple functions.

  • Callbacks (Functions as parameters). Functions can be used as parameters in another function, the passed function can then be called within the body of that function, that is why it is commonly called a callback.

    Callbacks refer to a mechanism in which a utility function provides a service to clients that are unknown to it when it is defined.

      package main
    
      import (
          "fmt"
      )
    
      func main() {
          fmt.Println(calculate(3, sum))
          fmt.Println(calculate(3, multiply))
      }
    
      func sum(a,b int) int {
          return a + b
      }
    
      func multiply(a,b int) int {
          return a * b
      }
    
      func calculate(x int, callback func(int, int) int) int{
          return callback(x, 8)
      }
    
      [output]
      11
      24
    

    Ref :

    1. Codewalk: First-Class Functions in Go.

results matching ""

    No results matching ""