DEV Community

Cover image for Learning Go - day 2
Magda Rosłaniec
Magda Rosłaniec

Posted on • Originally published at makneta.herokuapp.com

Learning Go - day 2

I should rather title it - part 2 not day 2 because I learned all those things in one day but it took me 4 days to write those notes and still they are not full. But writing is also part of learning.

1. For loops and if statements

for loops and if statements are quite simple to use. But to use them correctly we need to understand two things: blocks and scopes.

What's a block?

Code in Go can be divided into segments that are called blocks. Blocks are usually parts of the code between curly brackets. They can be nested. When we are creating a for loop, everything that's between curly brackets is a block. If we put if statements inside a for loop, every part of code between moustache (curly) brackets creates a block.

for num := 1; num <= 10; num++ {
    if num % 2 == 0 {
        fmt.Println(num, "is an even number.")
    } else if num % 2 != 0 {
    fmt.Println(num, "is an odd number.")
    }
}
Enter fullscreen mode Exit fullscreen mode

So there are 3 blocks of code in the example above.

What's the scope?

The scope of variables is connected with blocks. When we declare a variable, it will be recognizable only within a block in which we declared it and blocks there are nested inside that block. So when we declare a variable inside one part of an if statement, it won't work in another part of if statement as well as outside the if-else clause.

If I would like to change the previous code a bit and add a variable called answer like that:

for num := 1; num <= 10; num++ {        
    if num %2 == 0 {
        answer := "is an even number."
    } else if num%2 != 0 {
    answer := "is an odd number."       
    }       
    fmt.Println(num, answer)
}
Enter fullscreen mode Exit fullscreen mode

Go won't let me do it. For the first two "answers" it will tell that the variable is declared and never used. And for the last "answer" it will say that this is an undeclared name.

To fix it I need to declare the answer variable in the first block - for loop - like that:

for num := 1; num <= 10; num++ {    
    var answer string   
    if num %2 == 0 {
    answer = "is an even number."
    } else if num%2 != 0 {
    answer = "is an odd number."        
    }       
    fmt.Println(num, answer)
}
Enter fullscreen mode Exit fullscreen mode

What is continue and break?

Sometimes we want to finish the loop when a particular condition is met. So then we use the break statement.
Let's imagine that we're looking for a number divided by 5 in a range from 1 to 10 (10 is included). And we're happy with the very first number we get.

for n := 1; n <= 10;n++ {       
    if n % 5 == 0 {
        fmt.Println(n, "is divided by 5")
    break
    } else {
    fmt.Println(n, "is not divided by 5")
    }
}
Enter fullscreen mode Exit fullscreen mode

This short program will be running only as long as it comes to the very first number that can be divided by five: 5.

After running this loop (of course, it should be put inside the function to make it work in Go) we will see such an output:

1 is not divided by 5
2 is not divided by 5
3 is not divided by 5
4 is not divided by 5
5 is divided by 5

As we can see, my program stopped before checking all the numbers in the range.

Continue doesn't stop the loop, but if we have something after continue within the block in that statement, it won't check the code, only will go immediately to the next iteration of the loop.

for n := 1; n <= 10;n++ {       
    if n % 5 == 0 {
        fmt.Println(n, "is divided by 5")
    continue
        if (n == 5) {
        fmt.Println("It's 5.")
    }
    } else {
        fmt.Println(n, "is not divided by 5")
    }
}
Enter fullscreen mode Exit fullscreen mode

// output
1 is not divided by 5
2 is not divided by 5
3 is not divided by 5
4 is not divided by 5
5 is divided by 5
6 is not divided by 5
7 is not divided by 5
8 is not divided by 5
9 is not divided by 5
10 is divided by 5

The output shows that the block after continue wasn't executed at all.

2. There's a package for everything

So far, I haven't been using many methods in Go but have noticed that to use almost all the functions and methods that are provided by Go, I first need to import the packages that contain them.

If I want to print output, I need the fmt library and then one of the Print() functions. If I want to trim the next line character, I need the strings package and the TrimSpace() function.

But things are getting a bit complicated when we want to use methods that are connected to values of a certain type.

For example, when we want to get only a year from a date, we first have to import the time package. Then we will access the date using the Now() function that we need to assign to a variable. And only then we will use the method called Year().

package main 

import (
    "fmt"
    "time"
)

func main() {
    var now time.Time = time.Now();
    var year int = now.Year()   
    fmt.Println(year)   
}
Enter fullscreen mode Exit fullscreen mode

In that function, the first (now) variable's type is time.Time. The second (year) variable's type is an integer.

Some of those methods return 2 values while we need and have only one variable.
When we want to take input from a user, we need the bufio package and NewReader() function. In the code below I'm assigning bufio.NewReader(os.Stdin) to the reader variable.

reader := bufio.NewReader(os.Stdin)

The type of this variable is bufio.Reader. It's something very new to me. I was trying to print out the reader variable and it showed me something like that &{[0.....0] 0x0000e010 00 -1 -1}. At the moment I only understand that it's something binary.

In other to be able to use the input from the command line, the next step is to assign the reader variable to another variable but we also have to call another method - ReadString - on it.

input := reader.ReadString('\n') // it's got an error

The rune \n in the brackets means the end of input data.

But we are not ready yet. The ReadString method wants to return us 2 values but we have only one variable assigned. One value is the input from the command line but the other value is usually the information about errors. Now, we have to handle the error.

We will do it by adding the second variable:

fmt.Print("Give me a number: ")
reader := bufio.NewReader(os.Stdin)

input, err := reader.ReadString('\n') // error again

fmt.Println(input)
Enter fullscreen mode Exit fullscreen mode

The code won't compile again, because we always have to use all the declared values. We have two options.

  1. Use _ (underscore) instead of err.

input, _ := reader.ReadString('\n')

This way we will ignore all information about errors from input data.

  1. Or we can handle the error. Now we need one more package log and Fatal() function that will print us what error we have and will terminate our program.
input, err := reader.ReadString('\n')   
log.Fatal(err)  
fmt.Println(input)
Enter fullscreen mode Exit fullscreen mode

But again, using log.Fatal(err) like that will stop the program even though the command line user typed something.

We have to use if statement to show only when errors are not nil. (nil is a zero value - everything I know so far about nil)

package main

import (
    "fmt"
    "bufio"
    "os"
    "log"
)

func main() {
    fmt.Print("Give me a number: ")
    reader := bufio.NewReader(os.Stdin)

    input, err := reader.ReadString('\n')
    if err != nil {
        log.Fatal(err)
    }   
    fmt.Println(input)
}
Enter fullscreen mode Exit fullscreen mode

So many lines to only print the input data from the terminal.

One more thing to make packages more confusing ;-)

Sometimes we want to import a package, for example, rand for generating random numbers but in the part of the code with imports, we need to give the import path - math/rand. It's so because packages that are from similar categories are grouped. And rand package is group together with other libraries for mathematical operations.

package main 

import (
    "fmt"
    "math/rand"
)

func main() {
    target := rand.Intn(100) + 1
    fmt.Println(target)
}
Enter fullscreen mode Exit fullscreen mode

The code above generates a random number in the range from 1 to 100. But only in theory. If we want to run this, we could see the same number. Every time we will run it.

How to generate more random numbers? It's a story for another occasion.

Discussion (6)

Collapse
ddanielsantos profile image
Daniel Santos

Another great post Magda! Keep up the good work

Collapse
makneta profile image
Magda Rosłaniec Author

Thank you, Daniel!
Your nice words encourage me to take notes while reading/learning new things in Go.

Collapse
andrewbaisden profile image
Andrew Baisden

I can see that you are making good progress learning Go. Keep it up!

Collapse
makneta profile image
Magda Rosłaniec Author

Thank you, Andrew.

Collapse
willypuzzle profile image
Domenico Rizzo

The post is interesting even for me!

Collapse
makneta profile image
Magda Rosłaniec Author

Thank you for letting me know.