If-Statements
If-Statements: - Initializers: Allows you to generate a statement to set-up for a comparison - Variables in an if-statement are block-scoped - Comparison operators (primarily for numeric types): - less-than (<) - greater-than (>) - less-than-or-equal-to (<=) - greater-than-or-equal-to (>=) - double-equals operator (==) - not-equals operator (!=) - Logical operators: - And (&&) - Or (||) - Not (!) - Short Circuiting: Where Go-Lang skips comparison calculations - It chains tests with logical operators for an outcome - If an Or (||) operate evaluates a test to be True, it stops the comparison there - If an And (&&) operate evaluates a test to be False, it stops the comparison there - Equality & Floats: Better convert those floats because they don't play nice with comparisons!
func main() {
ygoDetails := map[string]string {
"Name": "Xie",
"DeckName": "Manipulstring",
"Dimension": "Xyz Dimension",
}
// Example #1
if true {
fmt.Println("The test is true!")
}
// Example #2
if pop, ok := ygoDetails["Name"]; ok {
fmt.Println(pop)
}
// Below line of code will cause an error because variables are block-scoped
// fmt.Println(pop)
}
Looping
Loops:
- For statements: Simple l00ps:
- for initializer; test; increment {}
- for test {}
- for {}
NOTE: The iteration variables in a loop are scoped to that block
- Exiting early:
- break (breaks out of a loop entirely)
- continue (skips an interation of a loop)
- labels (Allows you to use break out from an outer loop)
- Looping over collections:
- Arrays, slices, maps, strings, and channels can be looped through
- NOTE: Channels are a special case
- "for k, v := range collection {}" to get the key/value of an item in a collection
- Use "_" (underscore) as a throwaway variable to get EITHER the key/value
// Example 1: Basic loop
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// You can also change the increment value
for i := 0; i < 5; i = i+2 {
fmt.Println(i)
}
}
// Example 2: Having the initializer be outside the loop
// NOTE: You still need the first semi-colon when you do this
func main() {
i := 0
for ; i < 5; i++ {
fmt.Println(i)
}
// You can go further and put the incremental in the loop itself
// If you do, and you remove the initalizer too, you can remove the semi-colons completely
i = 0
for i < 10 {
fmt.Println(i)
i++
}
}
// Example 3: Simplifying EVERYTHING
func main() {
i = 0
for {
fmt.Println(i)
i++
// WARNING: Without this break checker, this loop will run forever
if i == 5 {
break
}
}
}
// Example 4: Continuing on
func main() {
for i := 10; i < 20; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i)
}
}
// Example 5: LABEL IT
func main() {
Loop:
for i := 1; i <= 3; i++ {
for j := i; j <= 3; j++ {
fmt.Println(i * j)
if i * j >= 3 {
break Loop
}
}
}
}
Switches
- Initializers: Allows you to generate a statement to set-up for a comparison - Switching on a tag - Cases with multiple tests - There can be NO OVERLAP in testing cases - Switches with no tags - There CAN be overlap in testing cases - The first case that evaluates to True gets used if there is overlap - Fallthrough keyword - While most other languages have explicit breaks and implicit fallthrough, - Go has the reverse: Implicit breaks and explicit fallthroughs - Keep in mind that using fallthrough will execute anything below it, regardless of any calculations - Type switches: To get the underlying types
// Example #1
func main() {
switch 5 {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
default:
fmt.Println("not one or two")
}
}
// Example #2
func main() {
switch 5 {
case 1, 5, 10:
fmt.Println("one, five, or ten")
case 2, 4, 6:
fmt.Println("two, four, or six")
default:
fmt.Println("another number")
}
}
// Example #3
func main() {
switch i := 2 + 4; i {
case 1, 5, 10:
fmt.Println("one, five, or ten")
case 2, 4, 6:
fmt.Println("two, four, or six")
default:
fmt.Println("another number")
}
}
// Example #4: Tagless syntax
func main() {
i := 10
switch {
case i <= 10:
fmt.Println("less than or equal to ten")
// Will force the below case to execute regardless of logic
// fallthrough
case i <= 20:
fmt.Println("less than or equal to twenty")
default:
fmt.Println("greater than twenty")
}
}
// Example #5: Type switching
func main() {
var e interface{} = 1 // An int
switch e.(type) {
case int:
fmt.Println("e is an int")
case float64:
fmt.Println("e is a float64")
case string:
fmt.Println("e is string")
default:
fmt.Println("e is another type")
}
}
Defer, Panic, and Recover
Defer: - Used to delay execution of a statement until function exits - Useful to group "open" and "close" functions together - Be careful in loops - Runs in LIFO (last-in, first-out) order - Arguments evaluated at time defer is executed, NOT at time of called function is executed Panic: - Occurs when program cannot continue at all - Don't use when file can't be opened, unless it is critical - Use for unrecoverable events - I.E cannot obtain TCP port for web server - Function will stop executing - Deferred functions will still fire - If nothing handles panic, program will exit Recover: - Used to recover from panics - Only useful in deferred functions - Current function will not attempt to continue, but higher functions in call stack will
Pointers
Creating pointers:
- Pointer types use an asterisk (*) as a prefix to type pointers to
- I.E *int - a point to an integer
- Use to addressof operator (&) to get the address of a variable
De-referencing pointers:
- De-reference a pointer by preceding with an asterisk (*)
- Complex types (I.E structs) are automatically de-referenced
Create pointers to objects:
- Can use the addressof operator (&) if value type already exists
- ms := myStruct{foo: 42}
- p := &ms
- Use addressof operator before initializer
- &myStruct{foo: 42}
- Use new keyword
- Can't initialize fields at the same time
Types with internal pointers:
- All assignment operations in Go are copy operations
- Slices and maps contain internal pointers, so copies point to same underlying data
d
