A Programmer's Guide To Go With LiteIDE
Written by Mike James   
Thursday, 14 November 2013
Article Index
A Programmer's Guide To Go With LiteIDE
Date Structures and Types
Functions
Go Control

Functions

Go isn't a class based hierarchically typed language and it doesn't do objects in the usual way. If you want to just write a function and ignore anything to do with objects you can. Functions are just values and in this sense they are what in other languages would be referred to as "first class objects". 

A function is declared using something like:

var myFunc = func(a, b int) int {
    return a + b
}

You can declare the type of the parameters and the return type. If you declare a return type then the function has to have a return statement. 

Notice the way the function value is assigned to the variable myFunc. You can use a more conventional form of function definition where the variable is specified as the function name:

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

In either case you could call the function using something like:

fmt.Println(myFunc(1, 2))

You can return multiple results in a return statement and name the variables to be returned in the function header. 

For example:

func myFunc(a, b int) (sum int) {
    sum = a + b
    return
}

In this case sum is named as an integer return value and it is automatically the value of the function when it returns. 

Returning multiple values is also easy:

func myFunc(a, b int) (int, int) {
    return a + b, a - b
}

In this case you have to accept both of the integer results:

   x,y := myFunc2(1, 4)
   fmt.Println(x,y)

You cannot opt to just have one of the two return values, which you can in some other languages. 

 

Pass By Value - Pointers

All parameters are passed by value and hence any changes you make to a parameter don't effect the value passed in. For example:

func myFunc(a, b int) int {
    a = 1
    return a + b
}

In this case the assignment in the function has no effect on the variable passed in as the argument for a. That is:

x, y := 2, 3
var sum = myFunc(x, y)
fmt.Println(sum, x)

gives the result 4 and 2 i.e. the value of x isn't changed.

If you want to allow functions to change their parameter values in the calling program you have to pass a pointer to the value. For example if we change the definition of the function to:

func myFunc(a *int, b int) int {
    *a = 1
    return *a + b
}

then the a parameter is passed as a pointer to the value and the assignment to *a changes whatever a is pointing at. In this case we also have to remember to pass the address of the first argument:

var sum = myFunc(&x, y)
fmt.Println(sum, x)

Now the result is 4 and 1 that is x has been changed by the function. 

This use of * and & should be familiar to any C programmer and it is the most primitive aspect of Go. There is an argument that in a modern language nearly everything should be passed by reference. 

Notice however that as the function definition demands a *int as its first parameter forgetting the & in a function call is picked up by the type checking at compile time - this is not the case in C. 

In short Go has pointer types so that you can pass pointers to values in functions rather than as a way to do clever things with data.

Scope And Closure

You can define functions inside functions as long as they are properly nested. Any variables that you declare exist within their enclosing block and any block within the enclosing block. This means you can define global variables outside of any function and any function declared within another function can access the variables in the outer function. 

For example:

var a1 int = 1
func main() {
    fmt.Println(a1)
    var a2 int = 2
    var myFunc = func() int {
        return a2
    }
    fmt.Println(myFunc())
}

In this case a1 is a global variable and is accessible from everywhere. The variable a2 is defined within main and is available everywhere within main including from within the function myFunc.

Go also supports closure. If you define a function within another function then the inner function has access to the variables of the outer function even if the outer function has terminated. The only way an inner function can persist after an outer function has terminated is if it is returned as a result of the outer function.

So for example: 

func myFunc() func() int {
    var a int = 1
    return func() int {
        return a
    }
} 

Notice that this is a function that returns a function of type func() int. Functions plus their signature, including the return types, are types. The function that is returned returns the value a which is defined in the outer function. It can do this because we have created a closure. 

So

myClosure := myFunc()
fmt.Println(myClosure())

prints the value 1.

Notice that each closure gets its own copy of the variables that it is bound to. That is closure isn't a way of sharing a state between multiple copies of a function. 

goruns

Go Control

Now that we know about data, type and functions the next important question is what control statements does Go provide? 

The answer is that Go has surprisingly few control constructs. In this respect it takes a minimalist approach. 

Go is a block structured language that uses curly brackets to group commands into blocks. If you have been wondering where the semi-colons so much used in other block languages have gone you might be surprised to learn that they are still there. The big difference is that Go puts the semi-colons in for you. If you put them in then the editor will simply take them out as they aren't actually necessary. 

The only loop that Go has is the for loop. This is used to create both a conditional and an enumeration loop. 

The conditional loop takes the form:

for condition { 
    instructions
}

 

Notice you don't need brackets around the condition. The loop continues until the condition is false. The condition is tested before each execution of the loop body - thus the loop body can execute zero or more times.and this is equivalent to a while statement.

For example:

i:=0

for a<10 { 
 fmt.print(a)
 a=a+1
}

 

You can create an infinite loop using for true { or just for {.

 

The enumeration loop is much the same as in any C like language:

for statement1 ; condition ; statement3 {
  instructions
}

 

Statement1 is executed once before the loop starts, statement3 is evaluated at the end of each execution of the loop body and the condition is evaluated before each execution of the loop body and it has to be true for the loop to continue. 

For example:

for i:=0; i<10; i++ {
 fmt.print(a)
}

 



Last Updated ( Thursday, 04 July 2019 )