go cli
go build # compiles a program
go run # used for compiling and executing a file
go fmt # formats all the code in each file in the current directory
go install # compiles and installs a package
go get # downloads the raw source code of someone else's package
go test # run and execute any tests
a go package is a collection of common source code files. The very first line of each file must declare the package that it belongs to.
inside of go there are 2 types of packages: an executable type and a reusable type. An executable type of package is the one that when compiled spits out an actual executable of runnable type of file. Executable packages are used for actually doing something.
Reusable packages are code dependencies or libraries.
Its actually the name of the package you use that determines whether you are making an executable or a reusable type package.
anytime we are using package main
we are using an executable package. Anything else and we are going to create a reusable package.
anytime we create an executable package, it must always have the function main inside itself.
import "fmt"
the import statement is used to give our package access to some code that is written inside another package. So its basically the same as having the import statement of python for instance.
fmt
is the name of the standard library package that is included by default with the go programming language.
Inside of go there are essentially 2 ways of defining a list like datastructure. The first one is called a good old array which can be of a fixed length only. The second one is called a slice that can be of variable length and has other properties. In slices however we can only store variables of the same type.
Object Oriented approach vs Go approach
Go is not an object oriented programming language and so there is no idea of classes in go In case of the deck of cards example that we are dealing with here in this project, an object oriented approach might be the following:
There would be a Deck class with the following data members:
- cards: string type storing all the cards
And the following methods:
- print: print the deck of cards
- shuffle: suffle the deck of cards
- saveToFile: save the entire deck to a file
However in case of Go, the approach would be a bit different:
We are going to have to create custom data types(which in theory are just classes). However unlike classes in which we can have our own data members, in case of go we literally extend characteristics of a base type.
For instance if we use type deck []string
we are saying that we have a special type deck which extends a type string
Analogous to methods we have receiver functions in go
One important thing to note when it comes to go packages is that if we have multiple files in the same folder, we can use functions declared in some other file. When we are using go run
, all we have to do is simply include that file as part of the go run
command.
Slicing in go works pretty much the same way as in python.
For a slice in go if we want to get a portion of it
we would do a[1:3]
which would give us back all elements from index 1 to index 2, it would not include 3
also a[:3]
would be from 0 to 2
also a[1:]
would be 1 till the end
Packages for interacting with files ioutil
byte slice
: byte slice is basically a string.
We can type cast strings to byte slices
greeting := "Hi there!"
fmt.Println([]byte(greeting))
func main() {
jim := person{
firstName: "Jim",
lastName: "Party",
contact: contactInfo{
email: "jim@gmail.com",
zipCode: 94000, // this comma is required
}, //even if it is the last value in the struct, the comma is required
}
jim.updateNamePassByValue("jimmy") // when we call this function, from the struct actually its a pass by value
jimPointer := &jim
jim.print()
jimPointer.updateNamePassByRef("jimmy")
jim.print()
}
//receiver functions for structs
func (p person) print() {
fmt.Printf("%+v", p)
}
func (p person) updateNamePassByValue(newFirstName string) {
p.firstName = newFirstName
}
func (p *person) updateNamePassByRef(newFirstName string) { // understand that p is here a pointer type
// object
(*p).firstName = newFirstName
}
The &
operator works in the same way as it works in c. It gives us the memory address of a variable
The *
operator also works in the same way as it works in c. It gives us the variable that is pointed to by the memory address
When we are calling the function updateNamePassByRef(newFirstName string)
, it is possible to actually
call the function simply using the object itself. for instance jim.updateNamePassByRef(<string>)
. go
would figure out that we are dealing with a pointer in the actual function and go will automatically
do the needful to give you a pointer inside the function.
For slices if we pass a slice to a function it is a pass by reference and not a pass by value.
Strings, floats, ints, structs, bools are all passed by value
When we actually instantiate and initialize a slice, go is actually creating 2 separate datastructures for us. The first one is a slice and the second one is an array.
The slice has a pointer, a capacity number and a length
. The length is how many elements are there in the slice. The capacity is how many elements the slice can hold and the ptr in the pointer to the head of the actual array
Whenever we create a slice, the slice and the array are existing in 2 separate locations in memory. Go is a pass by value language and so when we pass the slice over to the function, the slice is still copied however the new copied slice still has the pointer to the head as the pointer to the same array that was pointed to by the old slice. This makes the difference and this is why it appears that we are passing by reference.
This kind of behaviour is prevalent in many other data structures as well. These types
are called reference types.
Maps in go are basically like python dictionaries. One important thing is that in maps, all the keys must be of the same type and all the values must be of the same type. They can be declared statically in the following way:
static initialization of maps
colors := map[string]string {
"red": "<hex code of red>",
"blue": "<hex code of blue>",
}
var colors map[string]string // both keys and values are of type string
// another way of doing the same thing
colors := make(map[string]string)
colors["green"] = "<hex code for white>" // modifying an existing map
delete(colors, "green") // delete the "green" key
// iterating over maps
for color, hex := range colors {
fmt.Println("Hex code for the given color", color, "is", hex)
}
We cannot have same function names but different parameters in go. In other words function overloading
- Interfaces are not generic types. go(famously) does not support generic types.
- interfaces are implicit. This means that we do not have to explicitly say which variable is part of the interface
- interfaces are a contract to help us manage types.