- Arrays have fixed dimension that must be declared
- Slices don't need a fixed dimension
Arrays and Slices must be declared with a fixed size of how many elements fits indexed from 0..49 and the data type - all values must be same data type
Slices are an abstraction of an Array, variable-lenght or get sub-array of its own, Index-based and have size but resized when needed - it's a Dynamic Array
var bookings = [50]string{"Ale", "Palle", "Ugo"}
Array with values init, use {} for empty array
bookings[0] = firstName + " " + lastName // Assigning values to Array in specific positions
var bookings [50]string // Array init with no values
bookings := []string{} // We can use all the ways to declare variables with it's alternatives mixed
var bookings []string // Slice init with no values and no size
var bookings = make([]map[string]string, 0) // Initializing a list (Slice) of maps creating it with 'make' and specifying a size after , like 0 - Curly braces like Slices here don't work.
// Specifying a size won't matter but is required - being a Slice it will anyways grow in size automatically
** APPENDING **
bookings = append(bookings, firstName+" "+lastName) // Built-in Go Function that add elements at end of the slice and return updated slice value, grows the slice if more space is needed.
**** ANNOTATION VERBS (Placeholders) ****
Annotation verbs (or placeholders) can be used with 'Printf' function to format variables passed in
'%v' is the default format, there are others available to display value
All verbs can be found here: https://pkg.go.dev/fmt#hdr-Printing
**** CHANNELS ****
Allow for easy and safe communication between goroutines
Help when goroutines share data or are dependent on each other, mitigating concurrency issues
**** CONDITIONS ****
If - Else
Work as any other language, condition is either True or False (bool)
**** FUNCTIONS ****
func printFirstNames(bookings []string) {} // Declaring 'booking' slice parameter of type string in input for function
** RETURNING VALUES **
In Go you also have to define the oputput parameters including the type explicitly
Return parameteres goes after the input parameters out of the () brackets and must specify only the type
Multiple return output parameter types must be declared into another set of () brackets after the input parameters
func printFirstNames(bookings []string) []string {} // Declaring 'booking' slice parameter of type string in input and a Slice parameter of type string in output for function
func validateUserInput(firstName string, lastName string, email string, userTickets uint, remainingTickets uint) (bool, bool, bool) { // Declaring three outputs of 'bool' data type
return firstBool, secondBool, thirdBool // Returns can be separate with a comma
}
** NO INPUT ONLY RETURN **
func getUserInput () (string, string, string, uint) {}
** ASSIGNING RETURN **
isValidName, isValidEmail, isValidTicketNumber := validateUserInput(firstName, lastName, email, userTickets, remainingTickets)
**** GOROUTINES - THREADING & CONCURRENCY ****
We need to instanciate different Threads for things that might take long time
This allows to not block the code if one function takes longer or blocks
To run something in a spearate thread simply use 'go' in front of the statement/expression
'go' abstract the creation of a thread itself
With only 'go' the main thread (the application itself) exits and all the rest stops also.
WaitGroup waits for all the threads launched by the application to finish, it is part of the 'sync' package
Provides basic sync functionalities
WaitGroup has 3 functions
waitGroupName.Add() = Sets number of goroutines to wait (increases the counter by the provided number) - parameter is number of threads, must be excuted before instancing with 'go' and the params is how many functions are instanced with threads in the same block
waitGroupName.Wait() = Wait for all threads specified in 'Add()', waits until counter is 0 - must be executed ad the end of the main thread in the main function
waitGroupName.Done() = Executed in the function that runs a sperate thread to tell it's over with it and the thread can be killed - must be put at end of logic in function
**** MAPS ****
It's a collection of key, value pairs
Created with keyword 'map' and specifying types for [key] and 'value'
map[string]string // this only defines type of map
Maps unique keys to values, you can use the key later to retrieve that value
All keys have same data type - All values have the same data type
We need an expression to create the empty map which is 'make', a built-in function
make(map[string]string) // Invokes an expression that creates the map
**** VARIABLES AND CONSTANTS ****
You need to tell Go the data type always but when assigning a value and the data type will be in inferred
It is possible to define also another data type, more data types: https://www.tutorialspoint.com/go/go_data_types.htm
Specifying data types that fits the variable allows you to automatically validate the input
bookings[0] = firstName + " " + lastName // Assigning values to Array in specific positions
*** PACKAGE LEVEL VARIABLES ***
Variables outside function defined on top of everything
Globals, basically
Must be defined with 'var' keyword, cannot use ':='
**** OPERATORS & LOGIC ****
** USER INPUT VALIDATION **
isValidName := len(firstName) >= 2 && len(lastName) >= 2
isValidEmail := strings.Contains(email, "@")
isValidTicketNumber := userTickets > 0 && userTickets <= remainingTickets
** OPERATING ON VARIABLES**
remainingTickets = remainingTickets - userTickets // Operations on variables - both must be same type 'uint' because math between types 'uint' and 'int' cannot work
isValidTicketNumber := city == "Singapore" || city == "London"
** NEGATION **
!isValidTicketNumber := city == "Singapore" || city == "London"
isInvalidTicketNumber := city != "Singapore" || city != "London"
**** LOOPS ****
Go has only 'for' loop
Types of loops 'for' loops: For / For Each // For conditional
for {} - This runs forever is like 'for true'
for remainingTickets > 0 && len(booking) < 50 { }
** FOR EACH **
for index, value := range booking { }
To do a 'for each' we need to use 'range' which iterates over elements for different data structures (not only array/slices)
For arrays and slices 'range' provids index and value for each element
** ENDING LOOPS **
continue // Continue exit from this element without executing the code afterwards and continues to the next element
break // Break exits the loop
**** SCOPE ****
3 levels of scope
Local - Declared within the function or the block - can only be used in that function or block
Package - Declared outside all functions - can be also in different fiels but they all must be part of the same package
Global - Declared outside all functions and First Letter is Uppercase - can be used across all packages
**** SWITCH/CASE ****
Useful to execute different code for different or specific values when multiple are given
This solves the issue of having too many if's for each single case - allows variables to be tested for equality against list of values
Default solves the case if no match is found
city := "London"
switch city {
case "New York":
//code
case "Signapore":
//code
case "London", "Berlin": // Chain with comma, this code will be used for both the cases
//code
case "Mexico City":
//code
case "Hong Kong":
//code
default: // Case when no match is found
fmt.Print("No valid city selected")
}
**** STRINGS ****
"Fields" splits string with whitespace as separator - Return slice with split elements
var names = strings.Fields(booking)
**** STRUCTS ****
Collect different data types of data
Enables to to declare key, value pairs but with mixed data types
Define a custom Type where we can define what properties should have this structure
Like a Class but without inheritance
**** POINTERS ****
Also called "Special Variables"
A pointer is a variable that points to the memory address of another variable.
How it looks in memory:
Memory Address | Memory
Variable: tickets ---> 0xc01401 | '50' <--┐
0xc01402 | '..' | points to
Pointer: &tickets ---> 0xc01403 | '0xc01401' ---┘
We reference the memory address of a variable using '&' in front of the variable name
and read the value of the user input referencing the pointer in memory for that variable
fmt.Print("Please enter your first name: ") // Ask user for their name with Scan which gets the User Input
fmt.Scan(&firstName) // Assignment referencing the variable pointer
fmt.Print("Please enter your first name: ") // Ask user for their name with Scan which gets the User Input
We are getting user input passing to 'Scan' function not the Value of 'userName' which is empty but the reference, which will be the memory address
The Scan() function will read the user input and assign it to the user variable in memory
**** PRINT ****
Example of prints
Print belongs to fmt package.
Print() = Inline Print | Println() = Print new line | Printf() Print formatted text
fmt.Println("Welcome to", conferenceName, "booking application") // Use ',' to chain and puts automatically whitespace
fmt.Println("We have total of", conferenceTickets, "tickets and", remainingTickets, "are still available") // Multiple variables can be used
fmt.Printf("User %v booked %v tickets.\n", firstName, userTickets) // Multiple args print
fmt.Printf("conferenceTickets is %T, remainingTicket is %T, conferenceName is %T\n", conferenceTickets, remainingTickets, conferenceName) // The Placeholder '%T' displays the type
fmt.Printf("The whole array: %v\n", bookings)
fmt.Printf("The whole Array/Slice: %v\n", bookings)
fmt.Printf("The first value: %v\n", bookings[0])
fmt.Printf("Array/Slice type: %T\n", bookings)
fmt.Printf("Array/Slice lenght: %v\n", len(bookings))
**** SYNTACTIC SUGAR ****
'Syntatic Sugar' is the term to define features in a language that allows to do something more easily
Doesn't add functionality that it didn't already have eg.:
varName := "value" // Variable definition: no 'var' keyword and no type defined, use colon before assignation - No constants, No Types
fmt.Printf("varName has: '%v' assigned with Go-specific syntax\n", varName)
**** OTHER ****
Go has a distinction between "" and ''