Learning GO with Codeacademy
Kat 🐆🐾
Posted on November 28, 2020
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:
- A Tour of Go
- Effective Go
- The Go Programming Language Specification
- The Go Programming Language (book)
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
Alternatively, using go run
does combine both compilation and execution of code, but does not create an executable file:
go run file_name.go
Basic Go Structure: Packages
Let's look at the following code:
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
-
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 tellmain
to run its block of code. - The
main
function is special: a file that has apackage main
declaration will automatically run themain
function
- While we define the
Importing Multiple Packages
To import multiple packages, we can write
import "package1"
import "package2"
or
import (
"package1"
"package2"
)
Providing an alias to a package:
import (
p1 "package1"
"package2"
)
By using an alias, we can call functions from package1
by using p1
like this:
p1.SampleFunc()
instead of:
package1.SampleFunc()
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.
*/
Go Resources
Go has a built-in documentation system. Use the command go doc
followed by a package name, e.g.:
go doc fmt
Get more specific information about a function in a package, e.g:
go doc fmt.Println
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)
}
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)
}
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)
}
Basic Data Types in Go
- int
- float
- complex
Basic Numeric Types in Go
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
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)
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
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."
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."
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
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
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..."
quote, fact := "Bears, Beets, Battlestar Galactica", true
fmt.Println(quote) // Prints: Bears, Beets, Battlestar Galactica
fmt.Println(fact) // Prints: true
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?")
Println formats really well
Right?
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?
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?
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.
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.
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.
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!
fmt.Sprint()
doesn't include spaces or newline:
quote = fmt.Sprintln("Look ma,", "no spaces!")
fmt.Print(quote) // Prints Look ma, no spaces!
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)
Scan(): Getting User Input
fmt.Println("How are you doing?")
var response string
fmt.Scan(&response)
fmt.Printf("I'm %v.", response)
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!!")
}
isHungry := false
if (isHungry) {
fmt.Println("Eat the cookie")
} else {
fmt.Println("Step away from the cookie...")
}
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.")
}
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?")
}
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.
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")
}
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!")
}
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)
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))
}
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))
}
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
}
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
}
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
}
Since both parameters in the function above are the same, we could write it as:
func multiplier(x, y int32) int32 {
return x * y
}
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
}
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
}
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
}
}
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
Pointers
Pointers are used to store addresses.
var pointerForInt *int
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
Declare a pointer implicitly:
minutes := 55
pointerForInt := &minutes
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
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
}
Posted on November 28, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.