Control statements and functions
Control statements
if
if
doesn't need parentheses in Go.
Go allows us to initialize and use variables in if like this:
For multiple conditions we use the else if block
goto
Go has a goto
keyword, but be careful when you use it. goto
reroutes the control flow to a previously defined label
within the body of same code block.
The label name is case sensitive.
for
Go does not have while, do while. Just a for
, which is the most powerful control logic. It can read data in loops and iterative operations, just like while
. Like if
, for
doesn't need parenthesis.
We can omit one or more expressions.
Using for
like a while
break and continue
break
: jumps out of the loop. If you have nested loops, use break
along with labels.
continue
skips the current loop and starts the next one
for
can read data from slice
and map
when it is used together with range
.
Because Go supports multi-value returns and gives compile errors when you don't use values that were defined, as discussed earlier, _
is used to discard certain return values.
switch
Switch is an easier way to avoid long if-else
statements.
The type of sExpr
, expr1
, expr2
, and expr3
must be the same.
Conditions don't have to be constants and it checks the cases from top to bottom until it matches conditions and executes only the matching case.
If there is no statement after the keyword switch
, then it matches true
.
The default case is when there is no match to the switch.
Cases can have more than one values separated by a comma. By default switch executes only the matching case, however we can make switch execute the matching case and all cases below it using the fallthrough
statement.
This program prints the following information.
integer <= 6 integer <= 7 integer <= 8 default case
Functions
func
keyword is used to define a function.
Functions may have zero, one or more than one arguments. The argument type comes after the argument name and arguments are separated by
,
.Functions can return multiple values.
There are two return values named
output1
andoutput2
, you can omit their names and use their type only.If there is only one return value and you omitted the name, you don't need brackets for the return values.
If the function doesn't have return values, you can omit the return parameters altogether.
If the function has return values, you have to use the
return
statement somewhere in the body of the function.
A simple program to calculate maximum value.
In a function call, if two or more arguments have the same data type, then we can put the data type only after the last argument.
func max(a,b int, c,d string)
: this means we have four arguments, a,b: integers and c,d: string.
Multi-value return
SumAndProduct will return two values without names. Go allows us to have named return arguments. By using named arguments, the respective variables are returned automatically, we would just need to use return
.
If functions are going to be used outside of current program, it is better to explicitly write return statements for the sake of readability.
Variadic arguments to functions
In many cases, we do not know how many arguements can be passed, in such cases, we use variadic arguments.
func myfunc(arg ...int) {}
arg …int
tells Go that this is a function that has variable arguments. Notice that these arguments are type int
. In the body of function, the arg
becomes a slice
of int
.
for _, n := range arg { fmt.Printf("And the number is: %d\n", n) }
Pass by value and pointers
Argument are passed by value to the functions, the argument change inside the function doesn't affect the arguments used to call the function.
The original value of x
doesn't change, because we passed x as a value, so the function add1 created a copy of x. Despite having the same names, the both variables are totally independant of each other.
In cases where we want to be able to modify the argument's value, we use pass by reference using pointers.
In reality, a variable is nothing but a pointer to a location in memory. Each variable has a unique memory address. So, if we want to change the value of a variable, we must change its memory address. Therefore the function add1
has to know the memory address of x
in order to change its value. Here we pass &x
to the function, and change the argument's type to the pointer type *int
. Be aware that we pass a copy of the pointer, not copy of value.
Advantages of pointers:
Allows us to use more functions to operate on one variable.
Low cost by passing memory addresses (8 bytes), copy is not an efficient way, both in terms of time and space, to pass variables.
string
,slice
andmap
are reference types, so they use pointers when passing to functions by default. (Attention: If you need to change the length ofslice
, you have to pass pointers explicitly)
defer
Defer postpones the execution of a function till the calling function has finished executing. You can have many defer
statements in one function; they will execute in reverse order when the program reaches its end. In the case where the program opens some resource files, these files would have to be closed before the function can return with errors. Let's see some examples.
We saw some code being repeated several times. defer
solves this problem very well. It doesn't only help you to write clean code but also makes your code more readable.
If there are more than one defer
s, they will execute by reverse order. The following example will print 4 3 2 1 0
.
Functions as values and types
Functions are also variables in Go, we can use type
to define them. Functions that have the same signature can be seen as the same type.
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
This makes Go a functional language as functions are a first class citizen.
It's very useful when we use interfaces. As you can see testInt
is a variable that has a function as type and the returned values and arguments of filter
are the same as those of testInt
. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code.
Panic and Recover
Go doesn't have try-catch
structure like Java does. Instead of throwing exceptions, Go uses panic
and recover
to deal with errors. However, you shouldn't use panic
very much, although it's powerful.
Panic
is a built-in function to break the normal flow of programs and get into panic status. When a function F
calls panic
, F
will not continue executing but its defer
functions will continue to execute. Then F
goes back to the break point which caused the panic status. The program will not terminate until all of these functions return with panic to the first level of that goroutine
. panic
can be produced by calling panic
in the program, and some errors also cause panic
like array access out of bounds errors.
Recover
is a built-in function to recover goroutine
s from panic status. Calling recover
in defer
functions is useful because normal functions will not be executed when the program is in the panic status. It catches panic
values if the program is in the panic status, and it gets nil
if the program is not in panic status.
The following example shows how to use panic
.
The following example shows how to check panic
.
main and init functions
Go has two retentions which are called main
and init
, where init
can be used in all packages and main
can only be used in the main
package. These two functions are not able to have arguments or return values. Even though we can write many init
functions in one package, I strongly recommend writing only one init
function for each package.
Go programs will call init()
and main()
automatically, so you don't need to call them by yourself. For every package, the init
function is optional, but package main
has one and only one main
function.
Programs initialize and begin execution from the main
package. If the main
package imports other packages, they will be imported in the compile time. If one package is imported many times, it will be only compiled once. After importing packages, programs will initialize the constants and variables within the imported packages, then execute the init
function if it exists, and so on. After all the other packages are initialized, programs will initialize constants and variables in the main
package, then execute the init
function inside the package if it exists. The following figure shows the process.
import
import
is very often used in Go programs.
Methods of fmt
are called as follows.
fmt
is from Go standard library, it is located within $GOROOT/pkg. Go supports third-party packages in two ways.
Relative path
import "./model" // load package in the same directory, I don't recommend this way.
Absolute path
import "shorturl/model" // load package in path "$GOPATH/pkg/shorturl/model"
There are some special operators when we import packages, and beginners are always confused by these operators.
Dot operator
Sometimes we see people use following way to import packages.
The dot operator means you can omit the package name when you call functions inside of that package. Now fmt.Printf("Hello world")
becomes to Printf("Hello world")
.
Alias operation
It changes the name of the package that we imported when we call functions that belong to that package.
Now fmt.Printf("Hello world")
becomes to f.Printf("Hello world")
.
_
operator
This is the operator that is difficult to understand without someone explaining it to you.
The _
operator actually means we just want to import that package and execute its init
function, and we are not sure if want to use the functions belonging to that package.
Links
Last updated