DEV Community

Cover image for Learning GO with Codeacademy
Kat  🐆🐾
Kat 🐆🐾

Posted on

Learning GO with Codeacademy

Introduction

Go was developed at Google with the goal to eliminate the slowness and clumsiness of software development at Google, and thereby to make the process more productive and scalable. In other words, Go is supposed to make development faster and easier for developers.

It has modern features like garbage collection and also takes advantage of multi-core computer capabilities with built-in concurrency support.

Read up on why Go has become popular.

Other learning resources:

Compiling & Running Files

A compiler translates Go code into an executable/binary file.

Compile and run a file called file_name.go:

go build file_name.go
./file_name
Enter fullscreen mode Exit fullscreen mode

Alternatively, using go run does combine both compilation and execution of code, but does not create an executable file:

go run file_name.go
Enter fullscreen mode Exit fullscreen mode

Basic Go Structure: Packages

Let's look at the following code:

package main

import "fmt"

func main() {
  fmt.Println("Hello World")
}
Enter fullscreen mode Exit fullscreen mode
  • package main
    • This is a package declaration.
    • Every Go program starts with one.
    • It informs the compiler whether to create an executable or library.
    • A library doesn't execute code. It is a collection of code that can be used in other programs.
    • package main creates an executable file.
  • import "fmt"
    • import imports code from other packages
    • Packages are enclosed with double quotes "
  • func main() {...}
    • While we define the main function, we never explicitly tell main to run its block of code.
    • The main function is special: a file that has a package main declaration will automatically run the main function

Importing Multiple Packages

To import multiple packages, we can write

import "package1"
import "package2"
Enter fullscreen mode Exit fullscreen mode

or

import (
  "package1"
  "package2"
)
Enter fullscreen mode Exit fullscreen mode

Providing an alias to a package:

import (
  p1 "package1"
  "package2"
)
Enter fullscreen mode Exit fullscreen mode

By using an alias, we can call functions from package1 by using p1 like this:

p1.SampleFunc()
Enter fullscreen mode Exit fullscreen mode

instead of:

package1.SampleFunc()
Enter fullscreen mode Exit fullscreen mode

Comments

Go encourages the use of comments for describing what functions do. It gets used by Go's documentation tool.

// This is a comment
/*
This is a multiline
comment.
*/
Enter fullscreen mode Exit fullscreen mode

Go Resources

Go has a built-in documentation system. Use the command go doc followed by a package name, e.g.:

go doc fmt
Enter fullscreen mode Exit fullscreen mode

Get more specific information about a function in a package, e.g:

go doc fmt.Println
Enter fullscreen mode Exit fullscreen mode

Note: The capitalization of the function doesn't matter, this will also work: go doc fmt.println.

Fun Application

package main

import "fmt"

func main() {
  const gopher = 
"      `.-::::::-.`\n" +
"  .:-::::::::::::::-:.\n" +
"  `_:::    ::    :::_`\n" +
"   .:( ^   :: ^   ):.\n" +
"   `:::   (..)   :::.\n" +
"   `:::::::UU:::::::`\n" +
"   .::::::::::::::::.\n" +
"   O::::::::::::::::O\n" +
"   -::::::::::::::::-\n" +
"   `::::::::::::::::`\n" +
"    .::::::::::::::.\n" +
"      oO:::::::Oo\n"

  const dog = 
"  __      _\n" +
"o'')}____//\n" +
" `_/      )\n" +
" (_(_/-(_/\n"
  fmt.Println(gopher)
  fmt.Println(dog)
}
Enter fullscreen mode Exit fullscreen mode

Variables and Formatting

Example program showing

  • string
  • number (int8)
  • boolean
package main

import "fmt"

func main() {
  var stationName string
  var nextTrainTime int8
  var isUptownTrain bool

  stationName = "Union Square"
  nextTrainTime = 12
  isUptownTrain = false

  fmt.Println("Current station:", stationName)
  fmt.Println("Next train:", nextTrainTime, "minutes")
  fmt.Println("Is uptown:", isUptownTrain)
}
Enter fullscreen mode Exit fullscreen mode

Literals

Literals are values written into code "as they are", e.g. 109, or "hello world". They are unnamed values.

Constants and Variables

  • Constants cannot be updated while the program is running.
  • Variables can update their value
package main

import "fmt"

func main() {
  const earthsGravity = 9.80665

  fmt.Println(earthsGravity)
}
Enter fullscreen mode Exit fullscreen mode

Basic Data Types in Go

  • int
  • float
  • complex

Basic Numeric Types in Go

Screen Shot 2020-11-22 at 6.11.07 PM

Numeric types documentation

What is a Variable?

Variables are defined with the var keyword and two pieces of information: the name that we will use to refer to them and the type of data stored in the variable.

var lengthOfSong uint16
var isMusicOver bool
var songRating float32
Enter fullscreen mode Exit fullscreen mode

You can declare and assign a variable in one line, with or without declaring the type specifically:

var firstName = "Kat"
var middleName string = "Xenia"
fmt.Println(firstName + ", " + middleName)
Enter fullscreen mode Exit fullscreen mode

Zero Values

Even before we assign anything to our variables they hold a value. Go's designers attempted to make these "sensible defaults" that we can anticipate based on the variable's types.

var classTime uint32
var averageGrade float32
var teacherName string
var isPassFail bool

fmt.Println(classTime) // Prints 0
fmt.Println(averageGrade) // Prints 0
fmt.Println(teacherName) // Doesn't print anything, default: ""
fmt.Println(isPassFail) // Prints false
Enter fullscreen mode Exit fullscreen mode

Inferring Type

There is a way to declare a variable without explicitly stating its type using the short declaration := operator. We might use the := operator if we know what value we want our variable to store when creating it.

The following defines a bool, float, int, and string without specifying the type:

nuclearMeltdownOccurring := true
radiumInGroundWater := 4.521
daysSinceLastWorkplaceCatastrophe := 0
externalMessage := "Everything is normal. Keep calm and carry on."
Enter fullscreen mode Exit fullscreen mode

Floats created this way are of type float64. Integers created this way are either int32 or int64.

An alternative syntax to declare a variable and infer its type is:

var nuclearMeltdownOccurring = true
var radiumInGroundWater = 4.521
var daysSinceLastWorkplaceCatastrophe = 0
var externalMessage = "Everything is normal. Keep calm and carry on."
Enter fullscreen mode Exit fullscreen mode

Default int Type

There is one more common way to define an int in Go. Computers actually have a default length for the data in the Read-Only Memory (ROM). Some newer computers may have more processing power and can store/handle bigger chunks of data. These computers might be using a 64-bit architecture, but other computers still run on 320bit architecture and work just fine.

By providing the type int or uint, Go will check to see if the computer architecture is running on is 32-bit or 64-bit. Then it will either provide a 32-bit int (or uint) or a 64-bit one depending on the computer itself.

It is recommended to use int unless there's a reason to specify the size of the int (like knowing that value will be larger than the default type, or needing to optimize the amount of space used).

The following two variables are integers of either 32 or 64 bits:

var timesWeWereFooled int
var foolishGamesPlayed uint
Enter fullscreen mode Exit fullscreen mode

When a variable is declared and assigned a value using the := operator, it will be the same type as if it were declared as an int:

consolationPrizes := 2
Enter fullscreen mode Exit fullscreen mode

Multiple Variable Declaration

There are more than one way to declare multiple variables on a single line.

var part1, part2 string
part1 = "To be..."
part2 = "Not to be..."
Enter fullscreen mode Exit fullscreen mode
quote, fact := "Bears, Beets, Battlestar Galactica", true
fmt.Println(quote) // Prints: Bears, Beets, Battlestar Galactica
fmt.Println(fact) // Prints: true
Enter fullscreen mode Exit fullscreen mode

The fmt Package

fmt is one of Go's core packages. It helps format data, which is why it's sometimes referred to as the format package.

There are a few methods in the fmt package:

  • Printing data to the terminal
    • Println()
    • Print()
    • Printf()
  • Formatting (without printing to the terminal)
    • Sprint()
    • Sprintln()
    • Sprintf()
  • Getting user input
    • Scan()

The Print Method

fmt.Println() allows us to print to the terminal. It has some default styling built-in that makes viewing data easier. fmt.Println() prints its arguments with an included space in between each argument and adds a line break at the end.

Example:

fmt.Println("Println", "formats", "really well")
fmt.Println("Right?")
Enter fullscreen mode Exit fullscreen mode
Println formats really well
Right?
Enter fullscreen mode Exit fullscreen mode

In the case that default formatting is not wanted, fmt.Print() is more appropriate. It does not add default spacing, nor a line break.

The Printf Method

Using fmt.Println() and fmt.Print(), we have the ability to concatenate strings, i.e. combine different strings into a single string:

guess := "C"
fmt.Println("Is", guess, "your final answer?")
// Prints: Is C your final answer?
Enter fullscreen mode Exit fullscreen mode

With fmt.Printf(), we can interpolate strings, or leave placeholders in a string and use values to fill in the placeholders.

selection1 := "soup"
selection2 := "salad"
fmt.Printf("Do I want %v or %v?", selection1, selection2)
// Prints: Do I want soup or salad?
Enter fullscreen mode Exit fullscreen mode

The %v in the string is the placeholder and known as a verb in Go.

Different Verbs

In addition to %v, Go has a variety of useful verbs.

Example: %T prints out the type of a variable

specialNum := 42
fmt.Printf("This value's type is %T.", specialNum)
// Prints: This value's type is int.

quote := "To do or not to do"
fmt.Printf("This value's type is %T.", quote)
// Prints: This value's type is string.
Enter fullscreen mode Exit fullscreen mode

Example: %d interpolates a number into a strgin

votingAge := 18
fmt.Printf("You must be %d years old to vote.", votingAge)
// Prints: You must be 18 years old to vote. 
Enter fullscreen mode Exit fullscreen mode

Example: %f interpolates a float into a string, with the option to limit the precision:

gpa := 3.8
fmt.Printf("You're averaging: %f.", gpa)
// Prints: You're averaging 3.800000.

fmt.Printf("You're averaging: %.2f.", gpa)
// Prints: You're averaging 3.80.
Enter fullscreen mode Exit fullscreen mode

Sprint and Sprintln

fmt.Sprint() and fmt.Sprintln() format strings instead of printing them out.

The following example shows how fmt.Sprint() returns a value that can be stored in a variable and used later.

grade := "100"
compliment := "Great job!"
teacherSays := fmt.Sprint("You scored a ", grade, " on the test! ", compliment)

fmt.Print(teacherSays)
// Prints: You scored a 100 on the test! Great job!
Enter fullscreen mode Exit fullscreen mode

fmt.Sprint() doesn't include spaces or newline:

quote = fmt.Sprintln("Look ma,", "no spaces!")
fmt.Print(quote) // Prints Look ma, no spaces!
Enter fullscreen mode Exit fullscreen mode

The Sprintf Method

fmt.Sprintf() uses verbs just like fmt.Printf():

  template := "I wish I had a %v."
  pet := "puppy"

  var wish string

  // Add your code below:
  wish = fmt.Sprintf(template, pet)

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

Scan(): Getting User Input

fmt.Println("How are you doing?") 

var response string 
fmt.Scan(&response)

fmt.Printf("I'm %v.", response) 
Enter fullscreen mode Exit fullscreen mode

fmt.Scan()

  • reads in a string up until a whitespace
  • expects addresses for arguments

Conditionals

The if statement does not necessarily need parentheses:

alarmRinging := true
if alarmRinging {
  fmt.Println("Turn off the alarm!!") 
}
Enter fullscreen mode Exit fullscreen mode
isHungry := false
if (isHungry) {
  fmt.Println("Eat the cookie") 
} else {
  fmt.Println("Step away from the cookie...")
}
Enter fullscreen mode Exit fullscreen mode

Comparison & Logical Operators

To use a comparison operator, we have two values (or operands) with a comparison operator in between the two values.

if day == "Saturday" || day == "Sunday" {
  fmt.Println("Enjoy the weekend!")
} else {
  fmt.Println("Do some work.")
}
Enter fullscreen mode Exit fullscreen mode

else if Statement

position := 2

if position == 1 {
  fmt.Println("You won the gold!")
} else if position == 2 {
  fmt.Println("You got the silver medal.")
} else if position == 3 {
  fmt.Println("Great job on bronze.")
} else {
  fmt.Println("Sorry, better luck next time?")
}
Enter fullscreen mode Exit fullscreen mode

switch Statement

clothingChoice := "sweater"

switch clothingChoice {
case "shirt":
  fmt.Println("We have shirts in S and M only.")
case "polos":
  fmt.Println("We have polos in M, L, and XL.")
case "sweater":
  fmt.Println("We have sweaters in S, M, L, and XL.")
case "jackets":
  fmt.Println("We have jackets in all sizes.")
default:
  fmt.Println("Sorry, we don't carry that")
}
// Prints: We have sweaters in S, M, L, and XL.
Enter fullscreen mode Exit fullscreen mode

Scoped Short Declaration Statement

We can provide a short variable declaration before we provide a condition in either if or switch statements.

x := 8
y := 9
if product := x * y; product > 60 {
  fmt.Println(product, "  is greater than 60")
}
Enter fullscreen mode Exit fullscreen mode

Notice that the variable declaration is separated from the condition using a semi-colon ;.

switch season := "summer" ; season {
case "summer"
  fmt.Println("Go out and enjoy the sun!")
}
Enter fullscreen mode Exit fullscreen mode

The variable that is declared using the short variable declaration is scoped to the statement blocks. This means that the variable is only accessible within the blocks of those statements and not anywhere else.

This code will throw an error:

x := 8
y := 9
if product := x * y; product > 60 {
  fmt.Println(product, "  is greater than 60")
}
fmt.Println(product)
Enter fullscreen mode Exit fullscreen mode

Randomizing

Go has a math/rand library that has methods to generate random numbers.

import (
  "math/rand"
  "fmt"
)

func main() {
  fmt.Println(rand.Intn(100))
}
Enter fullscreen mode Exit fullscreen mode

The rand.Intn() method takes an argument, which is the maximum value that the method will return. It will return a random integer from 0 to 100.

Seeding

Go seeds (chooses) a number as a starting point for generating random numbers. By default, the seed value is 1. We can provide a new seed value using the method rand.Seed() and passing in an integer.

The seed number should always be a unique number, which can be achieved using time.

package main

import (
  "fmt"
  "math/rand"
  "time"
)

func main() {
  rand.Seed(time.Now().UnixNano())
  fmt.Println(rand.Intn(100))
}
Enter fullscreen mode Exit fullscreen mode

time.Now().UnixNano() results in the difference in time (in nanoseconds) since January 1st, 1970 UTC. Check out the UnixNano documentation for more details.

Functions

func doubleNum(num int) int {
  return num * 2
}
Enter fullscreen mode Exit fullscreen mode

The call site is the place where a function is called. Values that are returned from functions are set from within the function to the call site. A function can be given a return type.

func getLengthOfCentralPark() int32 {
  var lengthInBlocks int32
  lengthInBlocks = 51
  return lengthInBlocks
}
Enter fullscreen mode Exit fullscreen mode

Function parameters are variables that are used within the function. When calling a function, we give arguments, which are the values that we want those parameter variables to take.

func multiplier(x int32, y int32) int32 {
  return x * y
}
Enter fullscreen mode Exit fullscreen mode

Since both parameters in the function above are the same, we could write it as:

func multiplier(x, y int32) int32 {
  return x * y
}
Enter fullscreen mode Exit fullscreen mode

Scope

Scope keeps the namespace, the available variables and keywords, cleaner. Variables or functions can only be referred to in their respective namespace.

Multiple Return Values

func GPA(midtermGrade float32, finalGrade float32) (string, float32) {
  averageGrade := (midtermGrade + finalGrade) / 2
  var gradeLetter string
  if averageGrade > 90 {
    gradeLetter = "A"
  } else if averageGrade > 80 {
    gradeLetter = "B"
  } else if averageGrade > 70 {
    gradeLetter = "C"
  } else if averageGrade > 60 {
    gradeLetter = "D"
  } else {
    gradeLetter = "F"
  }

  return gradeLetter, averageGrade 
}
Enter fullscreen mode Exit fullscreen mode
func main() {
  var myMidterm, myFinal float32
  myMidterm = 89.4
  myFinal = 74.9
  var myAverage float32
  var myGrade string
  myGrade, myAverage = GPA(myMidterm, myFinal)
  fmt.Println(myAverage, myGrade) // Prints 82.12 B
}
Enter fullscreen mode Exit fullscreen mode

Deferring Resolution

We can delay a function call to the end of the current scope by using the defer keyword. defer tells Go to run a function, but at the end of the current function. This is useful for logging, file writing, and other utilities.

func calculateTaxes(revenue, deductions, credits float64) float64 {
  defer fmt.Println("Taxes Calculated!")
  taxRate := .06143
  fmt.Println("Calculating Taxes")

  if deductions == 0 || credits == 0 {
    return revenue * taxRate
  }


  taxValue := (revenue - (deductions * credits)) * taxRate
  if taxValue >= 0 {
    return taxValue
  } else {
    return 0
  }
}
Enter fullscreen mode Exit fullscreen mode

In the example above, the first print statement is being deferred. Normally, we would consider adding the print statement at the end of the function. But given that there are multiple return statements, instead of adding the same print statement before each return, defer can be used to print it regardless of when the function ends.

Addresses and Pointers

Go is a pass-by-value language, but it is possible to change values from different scopes using:

  • Addresses
  • Pointers
  • Dereferencing ### Addresses To find a variable's address we use the & operator followed by the variable name:
x := "My very first address"
fmt.Println(&x) // Prints 0x414020
Enter fullscreen mode Exit fullscreen mode

Pointers

Pointers are used to store addresses.

var pointerForInt *int
Enter fullscreen mode Exit fullscreen mode

The * operator signifies that this variable will store an address and the int portion means that the address contains an integer value.

var pointerForInt *int

minutes := 525600

pointerForInt = &minutes

fmt.Println(pointerForInt) // Prints 0xc000018038
Enter fullscreen mode Exit fullscreen mode

Declare a pointer implicitly:

minutes := 55

pointerForInt := &minutes
Enter fullscreen mode Exit fullscreen mode

Dereferencing

Dereferencing or indirecting refers to accessing the value at a given address.

lyrics := "Moments so dear" 
pointerForStr := &lyrics

*pointerForStr = "Journeys to plan" 

fmt.Println(lyrics) // Prints: Journeys to plan
Enter fullscreen mode Exit fullscreen mode

Changing Values in Different Scopes

By passing the value of a pointer, we can dereference the address and change the value in the function. The function call needs to pass in the address of the variable.

func addHundred (numPtr *int) {
  *numPtr += 100
}

func main() {
  x := 1
  addHundred(&x)
  fmt.Println(x) // Prints 101
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)