This repo is the result of my first time learning Go basics sintax and operations.
- Statically and StronglyTyped
- Is Compiled
- Built-in Concurrency
- Simplicity by design
- Garbage Collector
Package is just a folder that contains a collection of go files and a Collection of packages is known as a Module
A new project in Go (which is just a new module) can be done by
go mod init baby_steps
This creates a new file called go.mod that contais the module name and the go version that you are using
To our first step lets create a directory main/step1
the create a file called main.go
, so the file structure look like this:
.
├── go.mod
├── main
│ └── step1
│ └── main.go
Inside our main.go
let's start coding.
Remember that a go file needs to be inside a package.
so the first line that we're gonna write is
package main
and all the files inside this folder should have the same package name. Go docs give some package name guidelines here.
This package name main
is important because tells the go compiler the entrypoint of our applications is in this file.
With that, the go compiler will look for the main
function. which is required for packages with the main
name.
If we had another module called potato
it would not need a potato
function. This is only for main
package.
Ok, lets create out main function.
Functions in go are defined with the func
keyword followed by the name of the function
with its parameters beteween parenthesis. The block code of the function are inside curly-braces.
This will be something like this:
func main() {
//function code goes here
}
Lets import the fmt
module to help us print something in the terminal and then use it in the main
function to call Println
command. You have to use all the modules that you import, because the go compiler will slap you face if you have unused imports
import "fmt"
func main() {
fmt.Println("IDon'tHaveAGirlfriend")
}
Ok then, lets run this function with:
# this will create a new file called main.go
go build main/step1/main.go
#now we just call the created file
./main
Or you can just:
#This will build and execute your code in one command
go run main/step1/main.go
And that is it, we've crated out First Go code. Congratulations, I think:
They are declared starting with var
keyword, followed by the variable name and the type.
And just like the imports, the Go compiler scream if you have unused variables.
If you do set the value of the var when declarating one, Go will set default values.
If you are lazy, Go can infer the type and even let you remove the keyword var
if you use :=
func main() {
var potatoQuantity int = 666
fmt.Println(potatoQuantity)
//666
var anotherPotato int
fmt.Println(potatoQuantity)
// 0
var grape = 0 // now this a int
anotherGrape := 0 // shortest delcaration possible
ant, anotherAnt, uncleAnt := 0,2,4
}
We have multiple int
types.
Some like:
int8
, int16
, int32
, int64
: this defines the size of the number that will be stored in the memory.
Lets say that you want to use int8, which can store at max: number 127. If you add + 1 (now 128) the compiler will throw an overflow error. By default, if you put just int
it will assume at least 32bits value, based on you system.
So, if you have something like this:
package main
import "fmt"
func main() {
var potatoQuantity int8 = 128 //exceeds the max value of the type
fmt.Println(potatoQuantity)
}
You will receive the error
cannot use 128 (untyped int constant) as int8 value in variable declaration (overflows)
But this occurs only in compilation time. At execution time, if you do it at execution time, you will see something like this
func main() {
var potatoQuantity int8 = 127
// adding the value at runtime
fmt.Println(potatoQuantity + 1)
// >>> -128
}
So, be aware of this behaviour to avoid nights of debbuging. You`re welcome.
Another typw of int is a unsigned int: uint
that will allow you to store all positive values possible for the int type. You canhave a comination like: uint, uint32, unint8 etc
For floating numbers we have: float32
and float64
. as the in types, be aware of the data types because fi the value you stored in the type exceeds you will get the errors and weird behaviour that we saw previously.
As for arithmetic operations, you only can do it with var of the same type. You cannot do float32
+ int32
. But you can do a type casting to solve like:
func main() {
var potatoQuantity int32 = 127
var potatoPrice float32 = 12.2
fmt.Println(potatoQuantity * int32(potatoPrice))
}
strings are stored in the string
type 😛. And can declared with double quotes or back quotes.
Double quotes are for single lines, if you want to break a line you can add \n
. like
fmt.Println("Break the \nline")
With backquotes you can just hit enter and you just fine:
fmt.Println(`Break the
line`
)
For concatenations just:
fmt.Println("Concate"+"nated")
Be aware o how Go count the size of string. you can use the built-in len
fmt.Println(len("four"))
// 4
fmt.Println(len("fours"))
// 5
fmt.Println(len("©"))
// 2
fmt.Println(len("©©"))
// 2
But this fucntion calculate it based on its byte size. Since Go uses the UTF-8 enconding. So any char outside of the vanilla ASCII table are stored with more than a single byte
To avoid this pain you can use the built-in: unicode/utf8
and call utf8.RuneCountInString
:
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
fmt.Println(len("four"))
//4
fmt.Println(len("fours"))
//5
fmt.Println(utf8.RuneCountInString("©"))
//1
fmt.Println(utf8.RuneCountInString("©©"))
//2
}
just do
var boolean bool = true
Almost everything we said before apply to constants but, you cannot change their value because they are constants in case you did`nt noticed.
const dog = "Husky"
Its value must be defined at declaration time
The first curly brace of a function must be in the same line of its initial definition.
func main() {
insult("Lucas")
}
func insult(your_name string)
{
fmt.Print("My name is " + your_name + ", and I use Windows")
}
//main/step1/main.go:12:1: syntax error: unexpected semicolon or newline before {
As we already saw, wu must use the func
keyword followed by the name of the function and its parameters.
One Cool thing about the Go functions is that you can return multiple values delcaring like this:
func main() {
var result, remainder = divideAndReturnRemainder(8, 2)
fmt.Printf("result: %v. remainder: %v", result, remainder)
}
func divideAndReturnRemainder(number1 int, number2 int) (int, int) {
var result = number1 / number2
var remainder = number1 % number2
return result, remainder
}
divideAndReturnRemainder are the function that is returning multiple values.
But every body is gangsta until we use this function to divide by zero
divideAndReturnRemainder(8,0)
//panic: runtime error: integer divide by zeros
Of course we will receive an error, because, apparently math does not accept division by zero.
The Go way to handle this scenario is to have a return type error alongside with the no-error values.
error
is a built-in type in Go and its default value is nil
(that is the null
of Go).
Since out function returns an error. The caller have to check if this error is nil
or if it contains an error. Something like this:
func main() {
var result, remainder, err = divideAndReturnRemainder(8, 0)
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("result: %v. remainder: %v", result, remainder)
}
func divideAndReturnRemainder(number1 int, number2 int) (int, int, error) {
var err error
if number2 == 0 {
err = errors.New("math is not Mathing right now")
return 0, 0, err
}
var result = number1 / number2
var remainder = number1 % number2
return result, remainder, err
}
This pattern of error handling is common in Go, so if you import another package, they probably will be doing like us. So make sure that you check if err != nill
Nothing much to add. just take a look at the structure:
func main() {
var day = 3
switch day {
case 1:
fmt.Println("Monday")
case 2:
fmt.Println("Tuesday")
case 3:
fmt.Println("Wednesday")
case 4:
fmt.Println("Thursday")
case 5:
fmt.Println("Friday")
case 6:
fmt.Println("Saturday")
case 7:
fmt.Println("Sunday")
default:
fmt.Println("Invalid day")
}
}
As we already know, arrays are:
- Fixed Length
- Same Type
- Indexable (Start at index 0)
- Contiguous memory
The array below show how to declare one. And it obeys what we know about arrays.
func main() {
var intArr [3]int32
fmt.Println(intArr)
// [0 0 0]
intArr[2] = 26
fmt.Println(intArr)
// [0 0 26]
//Addr positions
fmt.Println(&intArr[0])
fmt.Println(&intArr[1])
fmt.Println(&intArr[2])
//0xc0000120a0
//0xc0000120a4
//0xc0000120a8
}
Under the hood slices are just arrays with some salse.
Slices are delcared almost like arrays, except you can omit the array size. And you can add 'infinity' things of the same type, using append(slice, elems)
func main() {
var sliceNumbers []int
sliceNumbers = append(sliceNumbers, 1)
sliceNumbers = append(sliceNumbers, 2, 3, 4)
}