11/02 Study Daily record

손진성·2022년 2월 11일
0

The reason for error handling is to prevent program errors that the compiler does not notice.

Then you need to set two things.
1) How to set the error value?
2) How to output and handle error conditions?

Errors in Go

  • Go does not have exceptions
  • It is good practice for functions to return errors
  • Errors in Go are children of a common interface

The Error Interface

type error interface {
	Error() string
}
  • Any type that implements function Error() string can be treated as an error
  • error type is the interface type taught in the 'interface' chapter. error interface has only one method with a string type of Error () returns a value.

Error() method prototype

func (e *errorString) Error() string {
	return e.s
}
  • If you look at the receiver part of this method, it directly accesses the address of the structure errorString and returns the field value s.

errorString structure

type errorString struct {
	s string
}
  • So let's see how we can initialize the pointer to the struct errorString. You can do this using the New() function of the "errors" package. If you call a function by inputting it in errors.New("error value") format, "error value" is converted to errorString structure type as shown below and a pointer is returned.
func New(text string) error {
	return &errorString(text)
}
  • Then, the Error() method automatically returns the error value contained in errorString as the flow above.

To return an Error

  • Use errors.New(),
  • Use fmt.Errorf(), or
  • Create your own error type

Change code

findSC

Before

func findSC(name, server string, c chan int) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Minute)

	//return security clearance from map
	c <- scMapping[name]
}

After

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Minute)

	if v, ok := scMapping[name]; !ok {
		return -1, errors.New("Crew member not found")
	} else {
		return v, nil
	}
}
  • It used to simulate a call to a database.
  • For the sake of this example we removed that channel argument that used to be here.
  • And instead we changed the returned results to have an int and an error offsets so the error is of the error interface that is the typical type for return for any result presenting an error.
  • So error will be nil if no errors are occurred.
  • A very obvious error scenario is if we do not find the data missing data
  • 'Mapname[key]' returns the value stored in the key as well as true/false values ​​that determine whether a value exists in the corresponding key, that is, whether it is the key value being used. Returns true if the key value is being used, false if not in use. And if a non-existent key value is entered, 0 or "" is returned depending on the data type.

Check when condition is false

if false {
	fmt.Println("False")
} else {
	fmt.Println("True")
}
True
  • when condition is false, if sentence prints false result(True).
  • go.dev/play/p/y6LCS8R8Siw

main

Before

func main() {

	rand.Seed(time.Now().UnixNano())

	c1 := make(chan int)
	c2 := make(chan int)

	name := "James"

	go findSC(name, "Server 1", c1)
	go findSC(name, "Server 2", c2)

	select {
	case sc := <-c1:
		fmt.Println(name, "has a security clearance of ", sc, "found in server1 ")
	case sc := <-c2:
		fmt.Println(name, "has a security clearance of ", sc, "found in server2 ")
	case <-time.After(1 * time.Millisecond):
		fmt.Println("Search timed out!!")
	}

}

After

func main() {
	rand.Seed(time.Now().UnixNano())
	clearance, err := findSC("Ruko", "Server 1")
	fmt.Println(" Clearance level found:", clearance, "Error code", err)
}
  • We'll start by seeding a random number generator as before
  • we just wrote with a name that doesn't exist in the SC mapping map
Clearance level found: -1 Error code Crew member not found
  • We get the expected result indicating that no crew member was found
  • As wee can see, the string message that passed from errors.New was shown.
  • go.dev/play/p/HJp1KhOmncs

Main

Before

After

func main() {
	rand.Seed(time.Now().UnixNano())
	if clearance, err := findSC("Ruko", "Server 1"); err != nil {
		fmt.Println("Error occured while searching for cleanrance level", err)
	} else {
		fmt.Println("Clearance level found:", clearance)
	}
}
  • We will call the function again findSC but this time inside the if statement. after the function is called we will check the err if it is nil or not. If there is not nil then we know there's an error.
  • Otherwise we print a value that we'll be obtained from our function.

Another way to use errors.New

var ErrCrewNotFound = errors.New("Crew member not found")

findSC and main Before

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)

	if v, ok := scMapping[name]; !ok {
		return -1, errors.New("Crew member not found")
	} else {
		return v, nil
	}
}
func main() {
	rand.Seed(time.Now().UnixNano())
	if clearance, err := findSC("Ruko", "Server 1"); err != nil {
		fmt.Println("Error occured while searching for cleanrance level", err)
	} else {
		fmt.Println("Clearance level found:", clearance)
	}
}

findSC and main After

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)

	if v, ok := scMapping[name]; !ok {
		return -1, ErrCrewNotFound
	} else {
		return v, nil
	}
}

func main() {
  rand.Seed(time.Now().UnixNano())
	clearance, err := findSC("Ruko", "Server 1")
	if err == ErrCrewNotFound{
		//handle the error
		fmt.Println("Confirmed error is crew not found!!")
	}
}
  • When we are returning the error variable which we declared, It represents our error.
  • We can then just check whether the error value returned from a function we're looking for.
  • there's a simple way to create an error that we cabn compre against.

Errorf

  • If you use the fmt.Errorf() function, you can create an error message with information about the value and parameter where an error occurred. Creating a message with a formatted string and multiple parameters is similar to fmt.Printf(). However, instead of outputting a message to the screen, an error type value containing the message is created.

fmt.Errorf()

func Errorf(format string, a ...interface{}) error {
    return errors.New(Sprintf(format, a...))
}

After

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)

	if v, ok := scMapping[name]; !ok {
		return -1, fmt.Errorf("Crew member %s could not be found on server '%s'", name, server)
	} else {
		return v, nil
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	if clearance, err := findSC("Ruko", "Server 1"); err != nil {
		fmt.Println("Error occured:", err)
	} else {
		fmt.Println("Clearance level found:", clearance)
	}
}
  • Let's pass the name and the server related to our search in this error message
Error occured: Crew member Ruko could not be found on server 'Server 1'
  • this time the error code message included more information about our issue.

find Error struct

type findError struct {
	Name, Server, Msg string
}

func (e findError) Error() string {
	return e.Msg
}
  • you're writing a complex program where we need to store multiple pieces of information about an error when it occurs.
  • For this example let's create a struct called findError which contains three strings name server and message
  • that is because the error type interface only includes the Error method.

After

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)

	if v, ok := scMapping[name]; !ok {
		return -1, findError{name, server, "Crew member not found"}
	} else {
		return v, nil
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	if clearance, err := findSC("Ruko", "Server 1"); err != nil {
		fmt.Println("Error occured while searching ", err)
		if v, ok := err.(findError); ok {
			fmt.Println("Server name", v.Server)
			fmt.Println("Crew member name", v.Name)
		}
	} else {
		fmt.Println("Crew member has security clearance:", clearance)
	}
}
  • go.dev/play/p/h_Nbbz8v9qd
  • We have a message field.
  • this time when we return an error, we create the new findError struct and assign all the releavant information to these fields
  • First we'll check whether the error is nil or not. just to ensure that there is an error.
  • we'll then use type assestion to try to retrieve the concrete type undereath the error interface.
  • We will go ahead and retrieve the values embedded in the concrete type which is the findeFrror struct and print them.
Error occured while searching  Crew member not found
Server name Server 1
Crew member name Ruko

Panic

func findSC(name, server string) (int, error) {
	//Simulate searching
	time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond))

	if v, ok := scMapping[name]; !ok {
		panic("Crew member not found")
	} else {
		return v, nil
	}
}

-You can invoke a panic by simply using the key word panic with a string pass as shown.

panic: Crew member not found

goroutine 1 [running]:
main.findSC({0x7a6e11, 0x4}, {0x55, 0x2547d93bb00})
C:/Go/Learngo/Udemy/main.go:30 +0x8d
main.main()
C://Learngo/Udemy/main.go:44 +0xb1
exit status 2

-It is due to the fact that we haven't recovered from the panic inside our code.

How to recover from Panic

defer func() {
	if err := recover(); err != nil {
		fmt.Println("A panic recovered", err)
	}
}()
  • Keyword there recover keyword has to be called from within a defer function because this is the only
  • place we can recover the value from the recover is not nil.
  • So if we run our code now we can see that we recovered.
A panic recovered Crew member not found
profile
Gopyther

0개의 댓글