if i > 0 { // Condition is True! i is greater than zero } else { // Condition is False! i is lower or equal to zero }
// Else if i := 1
if i > 0 { // Condition is True! i is greater than zero } elseif i > 0 && i < 2 { // Condition is True! i greater than zero and lower than two } elseif i > 1 && i < 4 { // Condition is True! i greater than one and lower than four } else { // None of the above conditions is True, so it falls here }
// If with short statements i := 2.567
if j := int(i); j == 2 { // Condition is True! j, the integer value of i, is equal to two } else { // Condition is False! j, the integer value of i, is not equal to two }
// Functions acts as a scoped block of code funcsayHello() { // Hello World! } sayHello() // Hello World!
// Functions can take zero or more parameters, as so return zero or more parameters funcsum(x int, y int)int { return x + y } sum(3, 7) // 10
// Returned values can be named and be used inside the function funcdoubleAndTriple(x int) (double, triple int) { double = x * 2 triple = x * 3 return } d, t := doubleAndTriple(5) // d = 10 // t = 15
// Skipping one of the returned values _, t := doubleAndTriple(3) // t = 9
// Functions can defer commands. Defered commands are // runned in a stack order after the execution and // returning of a function var aux = 0
funcswitchValuesAndDouble(x, y int) { aux = x defer aux = 0// cleaning variable to post use x = y * 2 y = aux * 2 }
a, b = 2, 5 switchValuesAndDouble(2, 5)
// a = 10 // b = 4 // aux = 0
// Functions can be handled as values and be anonymous functions funccalc(fn func(int, int)int) int { return fn(2, 6) }
funcsum(x, y int)int { return x + y }
funcmult(x, y int)int { return x * y }
calc(sum) // 8 calc(mult) // 12 calc( func(x, y int)int { return x / y } ) // 3
// Function closures: a function that returns a function // that remembers the original context funccalc()func(int)int { value := 0 returnfunc(x int)int { value += x return value } }
Interfaces are implicitly implemented. You don’t need to inform that your struct are correctly implementing a interface if it already has all methods with the same name of the interface. All structs implement the interface{} interface. This empty interface means the same as any.
1 2 3 4 5 6 7 8 9 10 11 12
// Car implements Vehicle interface type Vehicle interface { Accelerate() }
type Car struct {
}
func(car *Car) Accelerate() { return"Car is moving on ground" }
Go doesn’t support throw, try, catch and other common error handling structures. Here, we use error package to build possible errors as a returning parameter in functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import"errors"
// Function that contain a logic that can cause a possible exception flow funcfirstLetter(text string) (string, error) { iflen(text) < 1 { returnnil, errors.New("Parameter text is empty") } returnstring(text[0]), nil }
a, errorA := firstLetter("Wow") a // "W" errorA // nil
b, errorB := firstLetter("") b // nil errorB // Error("Parameter text is empty")
Go has a built-in library to unit testing. In a separate file you insert tests for functionalities of a file and run go test package to run all tests of the actual package or go test path to run a specific test file.
One of the main parts that make Go attractive is its form to handle with concurrency. Different than parallelism, where tasks can be separated in many cores that the machine processor have, in concurrency we have routines that are more lightweight than threads and can run asynchronously, with memory sharing and in a single core.
// Consider a common function, but that function can delay itself because some processing funcshow(from string) { for i := 0; i < 3; i++ { fmt.Printf("%s : %d\n", from, i) } }
// In a blocking way... funcmain() { show("blocking1") show("blocking2")
// Go routines are a function (either declared previously or anonymous) called with the keyword go funcmain() { go show("routine1") go show("routine2")
gofunc() { fmt.Println("going") }()
time.Sleep(time.Second)
fmt.Println("done") }
/* Obs: The result will depends of what processes first routine2: 0 routine2: 1 routine2: 2 going routine1: 0 routine1: 1 routine1: 2 done */
// Routines can share data with channels // Channels are queues that store data between multiple routines msgs := make(chanstring)
// Channels can be bufferized. Buffered channels will accept a limited number of values and when someone try to put belong their limit, it will throw and error numbers := make(chanint, 2)
msgs<-0 msgs<-1 msgs<-2
// fatal error: all goroutines are asleep - deadlock!
// Channels can be passed as parameter where the routine can only send or receive numbers := make(chanint)
// When working with multiple channels, the select can provide a control to execute code accordingly of what channel has bring a message c1 := make(chanstring) c2 := make(chanstring)
select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) default: fmt.Println("no messages") }
fmt.Print("Hello World") // Print in console fmt.Println("Hello World") // Print and add a new line in end fmt.Printf("%s is %d years old", "John", 32) // Print with formatting fmt.Errorf("User %d not found", 123) // Print a formatted error