Saturday, April 17, 2021

Golang - Flow control and error handling

Short Circuit Evaluation



According to Go's documentation, the sentence "The right operand is evaluated conditionally" points out that Go will only examine the expression if it is required. 


  // Go will check the second expression
  fmt.Println(true && false)

  // Go will only check the first expression if it returns false, 
// and the rest such as second expressions are not meaningful to check.
  fmt.Println(false && true)



If Statement



Unlike other languages, "Go" does not require parenthesis (unless where absolutely necessary).

The code block of a "if statement" is executed only if its condition expression is true.

Variables specified in this code block are also visible exclusively in this code block (block scope).


Error Handling



Everything does not always go as planned.

As a result, we must decide what to do when errors occur.

As an example:

1. A network problem or a downed web server
2. Incorrect input from users (missing some arguments? or by providing the incorrect type of input)


nil is a predefined identifier, similar to true and false, and indicates that the value has not yet been initialized.

Other languages have the similar identifier like nil:

1. javascript -> null
2. python -> None
3. jave -> null

nil is the zero value for pointer-based types:

1. pointers
2. slices
3. maps
4. interfaces
5. channels

We can utilize nil in 'Go' to determine whether or not a function answer contains an error.


    valueerror = getValueAndError()

    if error != nil {
        // Instead of processing the value, handle the error.
        // Then terminate the program
        return
    }

    // If there is no error, keep going



Error value and example



To better understand why we should handle errors, consider the following example.


  // We use 'ParseInt' because we assume users will input an integer
  valueerr := strconv.ParseInt(os.Args[1], 1064)

  fmt.Println("value ", value)
  fmt.Println("err: ", err)

 
The result is coming if users input an integer 1:

  value  1
  err:  <nil>


The result is coming if users input a string "hello":

  value  0
  err:  strconv.ParseInt: parsing "hello": invalid syntax


According to this example, if we did not check the "err" and access the value directly. Then we will get the incorrect value such as 0 in this example.


Simple Statement (Short If)



The standard approach to utilize a "if statement" is shown below.


  intValueerr := strconv.ParseInt(os.Args[1], 1064)
  if err != nil {
    return
  }

  fmt.Println("result", intValue)

 
We can use Simple Statement (Short If) to adjust it.


  if intValueerr := strconv.ParseInt(os.Args[1], 1064); err == nil {
    fmt.Println("result ", intValue)
  }


More Details:

1. ;, which is the separator, separate the simple statement and the condition expression.
2. The condition expression can use the declared variables inside the simple statement.
3. Declared variables are only visible inside the if statement (and its branches)

Consider the following complicated example to better grasp the scope of a simple statement:

 
  if args := os.Args; len(args) == 1 {
    fmt.Println("Input should not be empty")
  } else if intVerr := strconv.ParseInt(args[1], 1064); err != nil {
    fmt.Println("Invalid input")
  } else {
    fmt.Println("result "intV)
  }

 
'args' can be used by 'else if', and 'intValue' from 'else if' can be used by 'else'.


Shadowing



Let's revise the preceding example:


  intV := 0

  if args := os.Args; len(args) == 1 {
    fmt.Println("Input should not be empty")
  } else if intVerr := strconv.ParseInt(args[1], 1064); err != nil {
    fmt.Println("Invalid input")
  } else {
    fmt.Println("result ", intV)
  }

  fmt.Println("intValue:", intV)


Result:

result  1
  intV: 0


Because "if statement" creates its own code block, and Go detects that the variable intValue has already been declared outside of this scope, Go will construct a variable with the same name but only visible within this scope.

It's known as variable shadowing.


Switch



The "switch statement" searches for an equal value.


  month := "January"
  switch month {
  case "January":
    fmt.Println("It is Jan")
  case "Feburary":
    fmt.Println("It is Feb")
  }


More Details:

1. 'month' is a condition expression
2. Switch's condition expression determines the type of the 'case condition'
3. Case clause creates an exclusive block only for itself
4. The default clause is executed when no cases match.

It equals the following code (using the "if statement") from the preceding example:


  month := "January"
  if month == "January" {
    fmt.Println("It is Jan")
  }

 
More Details:

Only one case code block will be executed in Go.

Unlike other programming languages, Go will automatically include a 'break' statement at the end of each case code block. Refer to this doc.

In Go, just one case code block will be run.


Multiple conditions



In the switch case, you can specify multiple conditions.


  age := 17
  switch age {
  case 161718:
    fmt.Println("Senior High School")
  case 131415:
    fmt.Println("Junior High School")
  case 789101112:
    fmt.Println("Elementary School")
  }



Bool Expression



Another switch format supported by Go is shown below.

The bool statement can be used as a switch condition statement.

   
 score := 60

  switch true {
  case score >= 85:
    fmt.Println("A")
  case score >= 75:
    fmt.Println("B")
  case score >= 75:
    fmt.Println("C")
  case score >= 55:
    fmt.Println("D")
  default:
    fmt.Println("F")
  }

  
Because the switch expression is boolean (true), the case clause can also utilize bool expression.

We can also eliminate the switch expression because Go will set it to 'true' by default.

  
 score := 60

  switch {
  case score >= 85:
    fmt.Println("A")
  case score >= 75:
    fmt.Println("B")
  case score >= 75:
    fmt.Println("C")
  case score >= 55:
    fmt.Println("D")
  default:
    fmt.Println("F")
  }

  

Fallthrough switch



Refer to this doc.

The "fallthrough" statement instructs the flow to proceed to the next case block without checking the condition.

  
  switch i := 15; {
  case i > 20:
      fmt.Println("Hi-1")
      fallthrough
  case i > 10:
      fmt.Println("Hi-2")
      fallthrough
  case i > 5:
      fmt.Println("Hi-3")
  default:
      fmt.Println("Hi-4")
  }

  
Result:

Hi-2 Hi-3



Short Switch



It is similar idea like 'Short If'.

The standard format is provided below.

To separate the simple statement (score:= 60) and switch condition (true), we need to add the separator ';'.

 
  switch score := 60; true {
  case score >= 85:
    fmt.Println("A")
  case score >= 75:
    fmt.Println("B")
  case score >= 75:
    fmt.Println("C")
  case score >= 55:
    fmt.Println("D")
  default:
    fmt.Println("F")
  }


We can use the simplified version of Go to remove the switch condition (true).


  switch score := 60; {
  case score >= 85:
    fmt.Println("A")
  case score >= 75:
    fmt.Println("B")
  case score >= 75:
    fmt.Println("C")
  case score >= 55:
    fmt.Println("D")
  default:
    fmt.Println("F")
  }


Keep in mind that the separator ';' tells Go that this is a short switch, not a standard switch.


If V.S. Switch



If your "if statement" contains hard to read/understand code, try changing it to a "switch statement."


Loops



A block of code is repeated as long as its condition is true.


  for i := 1; i < 5; i++ {
    fmt.Println(i)
  }


More Details:

1. 'i := 1' is the initial state, which will be executed only once.
2. 'i < 5' is a bool condition expression, which will be checked before entering each loop step.
3. 'i++' is the post statement, which will be run after each step of the loop.

While Go does not support "while" syntax, you can use the format below with "break".


  i := 1

  for {
    if i >= 5 {
      break
    }

    fmt.Println(i)
    i++
  }



Using continue



By using the continue statement, you can skip the rest of the current phase.


Label Statement



We may come across a circumstance where we need to break the nested loop from inner to outer.

Consider the following example, which contains two nested "for loops" (one outer and one inner).


  words := strings.Fields(corpus)
  query := os.Args[1:]

// Outer Loop
  for _q := range query {
// Inner Loop
    for _w := range words {
      if q == w {
        fmt.Println("Found it", w)
        break
      }
    }
  }


Our goal is to exit this nested for loop when a condition in the inner loop is met.

How are we going to do it?

We can label it as "labeled break" and refer to it as such.

"Labeled break" breaks from the labeled statement.

  
 ords := strings.Fields(corpus)
  query := os.Args[1:]

outerLoop:
  for _q := range query {
    for _w := range words {
      if q == w {
        fmt.Println("Found it", w)
        break outerLoop
      }
    }
  }


The same as "labeled break," we may use "labeled continue" to exist the inner loop while continuing to execute the outer loop.

 
ords := strings.Fields(corpus)
  query := os.Args[1:]

outerLoop:
  for _q := range query {
    for _w := range words {
      if q == w {
        fmt.Println("Found it", w)
        continue outerLoop
      }
    }
  }

 
If there is a switch inside a for loop, the break command will only exist as a switch code block, and the rest of the code inside the for loop will be performed continuously.

 
ords := strings.Fields(corpus)
  query := os.Args[1:]

outerLoop:
  for _q := range query {

    for _w := range words {
switch (w) {
 case "pass":   break; }
      if q == w {
        fmt.Println("Found it", w)
        continue outerLoop
      }
    }
  }

 

Then we can use labeled break to keep the remaining code in the loop.

 
ords := strings.Fields(corpus)
  query := os.Args[1:]

outerLoop:
  for _q := range query {
innerLoop:
    for _w := range words {
switch (w) {
 case "pass":   break innerLoop }
      if q == w {
        fmt.Println("Found it", w)
        continue outerLoop
      }
    }
  }

 

Goto



Navigate to a label.

It can hop practically anywhere inside the same function to any label.

Here's an example of how to use Goto to create a for loop.


  counter := 0

myLoop:
  if counter < 5 {
    counter++
    fmt.Println("Counter++")
    goto myLoop
  }

  fmt.Println("Final Counter", counter)


No comments:

Post a Comment