Reader small image

You're reading from  Go Programming - From Beginner to Professional - Second Edition

Product typeBook
Published inMar 2024
Reading LevelBeginner
PublisherPackt
ISBN-139781803243054
Edition2nd Edition
Languages
Right arrow
Author (1)
Samantha Coyle
Samantha Coyle
author image
Samantha Coyle

Samantha Coyle, a Software Engineer at Diagrid, specializes in Go for cloud-native developer tooling, abstracting application development challenges. Committed to Open Source, she contributes to projects like Dapr and Testcontainers. She boasts a rich history in retail computer vision solutions and successfully stabilized industrial edge use cases with testing and diverse deployments for biopharma data pipelines. Her expertise extends to being CKAD certified and reviewing Go textbooks. She is passionate about empowering early-career, diverse professionals. Samantha is in a family of gophers, and enjoys GopherCon with her brother and identical twin sister. She's a seasoned speaker, having presented at various conferences, including GopherCon.
Read more about Samantha Coyle

Right arrow

What does Go look like?

Let’s take our first look at some Go code. This code randomly prints a message to the console from a pre-defined list of messages:

package main
// Import extra functionality from packages
import (
  "errors"
  "fmt"
  "log"
  "math/rand"
  "strconv"
  "time"
)// Taken from: https://en.wiktionary.org/wiki/Hello_World#Translations
var helloList = []string{
  "Hello, world",
  "Καλημέρα κόσμε",
  "こんにちは世界",
  "سلام دنیا‎",
  "Привет, мир",
}

The main() function is defined as follows:

func main() {
  // Seed random number generator using the current time
  rand.NewSource(time.Now().UnixNano())
  // Generate a random number in the range of out list
  index := rand.Intn(len(helloList))
  // Call a function and receive multiple return values
  msg, err := hello(index)
  // Handle any errors
  if err != nil {
    log.Fatal(err)
  }
  // Print our message to the console
  fmt.Println(msg)
}

Let’s consider the hello() function:

func hello(index int) (string, error) {
  if index < 0 || index > len(helloList)-1 {
    // Create an error, convert the int type to a string
    return "", errors.New("out of range: " + strconv.Itoa(index))
  }
  return helloList[index], nil
}

Now, let’s step through this code piece by piece.

At the top of our script is the following:

package main

This code is our package declaration. All Go files must start with one of these. If you want to run the code directly, you’ll need to name it main. If you don’t name it main, then you can use it as a library and import it into other Go code. When creating an importable package, you can give it any name. All Go files in the same directory are considered part of the same package, which means all the files must have the same package name.

In the following code, we’re importing code from packages:

// Import extra functionality from packages
import (
  "errors"
  "fmt"
  "log"
  "math/rand"
  "strconv"
  "time"
)

In this example, the packages are all from Go’s standard library. Go’s standard library is very high-quality and comprehensive. It’s strongly recommended that you maximize your use of it. You can tell if a package isn’t from the standard library because it’ll look like a URL – for example, github.com/fatih/color.

Go has a module system that makes using external packages easy. To use a new module, add it to your import path. Go will automatically download it for you the next time you build code.

Imports only apply to the file they’re declared in, which means you must declare the same imports over and over in the same package and project. But fear not – you don’t need to do this by hand. There are many tools and Go editors that automatically add and remove the imports for you:

// Taken from: https://en.wiktionary.org/wiki/Hello_World#Translations
var helloList = []string{
  "Hello, world",
  "Καλημέρα κόσμε",
  "こんにちは世界",
  "سلام دنیا‎",
  "Привет, мир",
}

Here, we’re declaring a global variable, which is a list of strings, and initializing it with data. The text or strings in Go support multi-byte UFT-8 encoding, making them safe for any language. The type of list we’re using here is called a slice. There are three types of lists in Go: slices, arrays, and maps. All three are collections of keys and values, where you use the key to get a value from the collection. Slice and array collections use a number as the key. The first key is always 0 in slices and arrays. Also, in slices and arrays, the numbers are contiguous, which means there is never a break in the sequence of numbers. With the map type, you get to choose the key type. You use this when you want to use some other data to look up the value in the map. For example, you could use a book’s ISBN to look up its title and author:

func main() {
…
}

Here, we’re declaring a function. A function is some code that runs when called. You can pass data in the form of one or more variables to a function and optionally receive one or more variables back from it. The main() function in Go is special. The main() function is the entry point of your Go code. There may only be one main() function within the main package. When your code runs, Go automatically calls main to get things started:

  // Seed random number generator using the current time
  rand.Seed(time.Now().UnixNano())
  // Generate a random number in the range of out list
  index := rand.Intn(len(helloList))

In the preceding code, we are generating a random number. The first thing we need to do is ensure it’s a good random number; to do that, we must seed the random number generator. We seed it using the current time formatted to a Unix timestamp with nanoseconds. To get the time, we call the Now function in the time package. The Now function returns a struct type variable. Structs are a collection of properties and functions, a little like objects in other languages. In this case, we are calling the UnixNano function on that struct straight away. The UnixNano function returns a variable of the int64 type, which is a 64-bit integer or, more simply, a number. This number is passed into rand.Seed. The rand.Seed function accepts an int64 variable as its input. Note that the type of the variable from time.UnixNano and rand.Seed must be the same. With that, we’ve successfully seeded the random number generator.

What we want is a number we can use to get a random message. We’ll use rand.Intn for this job. This function gives us a random number between 0 and 1, minus the number we pass in. This may sound a bit strange, but it works out perfectly for what we’re trying to do. This is because our list is a slice where the keys start from 0 and increment by 1 for each value. This means the last index is 1 less than the length of the slice.

To show you what this means, here is some simple code:

package main
import (
  "fmt"
)
func main() {
  helloList := []string{
    "Hello, world",
    "Καλημέρα κόσμε",
    "こんにちは世界",
    "سلام دنیا‎",
    "Привет, мир",
  }
  fmt.Println(len(helloList))
  fmt.Println(helloList[len(helloList)-1])
  fmt.Println(helloList[len(helloList)])
}

This code prints the length of the list and then uses that length to print the last element. To do that, we must subtract 1; otherwise, we’d get an error, which is what the last line causes:

Figure 1.1: Output displaying an error

Figure 1.1: Output displaying an error

Once we’ve generated our random number, we assign it to a variable. We do this with the short variable declaration seen with the := notation, which is a very popular shortcut in Go within a function. It tells the compiler to go ahead and assign that value to the variable and select the appropriate type for that value implicitly. This shortcut is one of the many things that makes Go feel like a dynamically typed language:

  // Call a function and receive multiple return values
  msg, err := hello(index)

Then, we use that variable to call a function named hello. We’ll look at hello in just a moment. The important thing to note is that we’re receiving two values back from the function and we’re able to assign them to two new variables, msg and err, using the := notation and with err as the second value:

func hello(index int) (string, error) {
…
}

This code is the definition of the hello function; we’re not showing the body for now. A function acts as a unit of logic that’s called when and as often as is needed. When calling a function, the code that calls it stops running and waits for the function to finish running. Functions are a great tool for keeping your code organized and understandable. In the signature of hello, we’ve defined that it accepts a single int value and that it returns a string value and an error value. Having error as your last return value is a very common thing to have in Go. The code between {} is the body of the function. The following code is what’s run when the function’s called:

  if index < 0 || index > len(helloList)-1 {
    // Create an error, convert the int type to a string
    return "", errors.New("out of range: " + strconv.Itoa(index))
  }
  return helloList[index], nil

Here, we are inside the function; the first line of the body is an if statement. An if statement runs the code inside its {} if its Boolean expression is true. The Boolean expression is the logic between if and {. In this case, we’re testing to see if the passed index variable is less than 0 or greater than the largest possible slice index key.

If the Boolean expression were to be true, then our code would return an empty string and an error value. At this point, the function would stop running, and the code that called the function would continue to run. If the Boolean expression were not true, its code would be skipped over, and our function would return a value from helloList and nil. In Go, nil represents something with no value and no type:

  // Handle any errors
  if err != nil {
    log.Fatal(err)
  }

After we’ve run hello, the first thing we need to do is check if it ran successfully. We can do this by checking the error value stored in err. If err is not equal to nil, then we know we have an error. You will see checks on whether err is not equal to nil as opposed to checks on whether err is equal to nil, as this simplifies the checks and logic for the code base. In the case of an error, we call log.Fatal, which writes out a logging message and kills our app. Once the app’s been killed, no more code runs:

  // Print our message to the console
  fmt.Println(msg)

If there is no error, then we know that hello ran successfully and that the value of msg can be trusted to hold a valid value. The final thing we need to do is print the message to the screen via the Terminal.

Here’s how that looks:

Figure 1.2: Output displaying valid values

Figure 1.2: Output displaying valid values

In this simple Go program, we’ve been able to cover a lot of key concepts that we’ll explore in full in the coming chapters.

Exercise 1.01 – using variables, packages, and functions to print stars

In this exercise, we’ll use some of what we learned about in the preceding example to print a random number, between 1 and 5, of stars (*) to the console. This exercise will give you a feel of what working with Go is like and some practice with using the features of Go we’ll need going forward. Let’s get started:

  1. Create a new folder and add a main.go file to it.
  2. In main.go, add the main package name to the top of the file:
    package main
  3. Now, add the imports we’ll use in this file:
    import (
      "fmt"
      "math/rand"
      "strings"
      "time"
    )
  4. Create a main() function:
    func main() {
  5. Seed the random number generator:
      rand.Seed(time.Now().UnixNano())
  6. Generate a random number between 0 and then add 1 to get a number between 1 and 5:
      r := rand.Intn(5) + 1
  7. Use the string repeater to create a string with the number of stars we need:
      stars := strings.Repeat("*", r)
  8. Print the string with the stars to the console with a new line character at the end and close the main() function:
      fmt.Println(stars)
    }
  9. Save the file. Then, in the new folder, run the following:
    go run .

The following is the output:

Figure 1.3: Output displaying stars

Figure 1.3: Output displaying stars

In this exercise, we created a runnable Go program by defining the main package with a main() function in it. We used the standard library by adding imports to packages. Those packages helped us generate a random number, repeat strings, and write to the console.

Activity 1.01 – defining and printing

In this activity, we are going to create a medical form for a doctor’s office to capture a patient’s name, age, and whether they have a peanut allergy:

  1. Create a variable for the following:
    1. First name as a string.
    2. Family name as a string.
    3. Age as an int value.
    4. Peanut allergy as a bool value.
  2. Ensure they have an initial value.
  3. Print the values to the console.

The following is the expected output:

Figure 1.4: Expected output after assigning the variables

Figure 1.4: Expected output after assigning the variables

Note

The solution to all activities in this chapter can be found in the GitHub repository here: https://github.com/PacktPublishing/Go-Programming-From- Beginner-to-Professional-Second-Edition-/tree/main/Chapter01

Next, we’ll start going into detail about what we’ve covered so far, so don’t worry if you are confused or have any questions about what you’ve seen so far.

Previous PageNext Page
You have been reading a chapter from
Go Programming - From Beginner to Professional - Second Edition
Published in: Mar 2024Publisher: PacktISBN-13: 9781803243054
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Samantha Coyle

Samantha Coyle, a Software Engineer at Diagrid, specializes in Go for cloud-native developer tooling, abstracting application development challenges. Committed to Open Source, she contributes to projects like Dapr and Testcontainers. She boasts a rich history in retail computer vision solutions and successfully stabilized industrial edge use cases with testing and diverse deployments for biopharma data pipelines. Her expertise extends to being CKAD certified and reviewing Go textbooks. She is passionate about empowering early-career, diverse professionals. Samantha is in a family of gophers, and enjoys GopherCon with her brother and identical twin sister. She's a seasoned speaker, having presented at various conferences, including GopherCon.
Read more about Samantha Coyle