diff --git a/examples.txt b/examples.txt index b637704b8..1607fcdec 100644 --- a/examples.txt +++ b/examples.txt @@ -22,6 +22,8 @@ Interfaces Struct Embedding Generics Errors +Wrapping and Sentinel Errors +Custom Errors Goroutines Channels Channel Buffering diff --git a/examples/custom-errors/custom-errors.go b/examples/custom-errors/custom-errors.go new file mode 100644 index 000000000..89dcbc041 --- /dev/null +++ b/examples/custom-errors/custom-errors.go @@ -0,0 +1,47 @@ +// It's possible to use custom types as `error`s by +// implementing the `Error()` method on them. Here's a +// variant on the example above that uses a custom type +// to explicitly represent an argument error. + +package main + +import ( + "errors" + "fmt" +) + +// It's possible to use custom types as `error`s by +// implementing the `Error()` method on them. Here's a +// variant on the example above that uses a custom type +// to explicitly represent an argument error. +// A custom error type has usualy the suffix "Error". +type argError struct { + arg int + message string +} + +func (e *argError) Error() string { + return fmt.Sprintf("%d - %s", e.arg, e.message) +} + +func f(arg int) (int, error) { + if arg == 42 { + // In this case we use `&argError` syntax to build + // a new struct, supplying values for the two + // fields `arg` and `message`. + return -1, &argError{arg, "can't work with it"} + } + return arg + 3, nil +} + +func main() { + // If you want to programmatically use the data in + // a custom error, you'll need to get the error as an + // instance of the custom error with errors.As() + _, err := f(42) + var ae *argError + if errors.As(err, &ae) { + fmt.Println(ae.arg) + fmt.Println(ae.message) + } +} diff --git a/examples/custom-errors/custom-errors.hash b/examples/custom-errors/custom-errors.hash new file mode 100644 index 000000000..d7c9dd54e --- /dev/null +++ b/examples/custom-errors/custom-errors.hash @@ -0,0 +1,2 @@ +bb8fb48c2d05f29aa87ea21616ff042b52d497c3 +sD36hYfgf__q diff --git a/examples/custom-errors/custom-errors.sh b/examples/custom-errors/custom-errors.sh new file mode 100644 index 000000000..47772ac2b --- /dev/null +++ b/examples/custom-errors/custom-errors.sh @@ -0,0 +1,5 @@ +$ go run errors.go +f worked: 10 +f failed: 42 - can't work with it +42 +can't work with it diff --git a/examples/errors/errors.go b/examples/errors/errors.go index b8501392d..9d3b17ebf 100644 --- a/examples/errors/errors.go +++ b/examples/errors/errors.go @@ -16,13 +16,11 @@ import ( // By convention, errors are the last return value and // have type `error`, a built-in interface. -func f1(arg int) (int, error) { +func f(arg int) (int, error) { if arg == 42 { - // `errors.New` constructs a basic `error` value // with the given error message. return -1, errors.New("can't work with 42") - } // A `nil` value in the error position indicates that @@ -30,58 +28,16 @@ func f1(arg int) (int, error) { return arg + 3, nil } -// It's possible to use custom types as `error`s by -// implementing the `Error()` method on them. Here's a -// variant on the example above that uses a custom type -// to explicitly represent an argument error. -type argError struct { - arg int - prob string -} - -func (e *argError) Error() string { - return fmt.Sprintf("%d - %s", e.arg, e.prob) -} - -func f2(arg int) (int, error) { - if arg == 42 { - - // In this case we use `&argError` syntax to build - // a new struct, supplying values for the two - // fields `arg` and `prob`. - return -1, &argError{arg, "can't work with it"} - } - return arg + 3, nil -} - func main() { - // The two loops below test out each of our // error-returning functions. Note that the use of an // inline error check on the `if` line is a common // idiom in Go code. for _, i := range []int{7, 42} { - if r, e := f1(i); e != nil { - fmt.Println("f1 failed:", e) + if r, e := f(i); e != nil { + fmt.Println("f failed:", e) } else { - fmt.Println("f1 worked:", r) + fmt.Println("f worked:", r) } } - for _, i := range []int{7, 42} { - if r, e := f2(i); e != nil { - fmt.Println("f2 failed:", e) - } else { - fmt.Println("f2 worked:", r) - } - } - - // If you want to programmatically use the data in - // a custom error, you'll need to get the error as an - // instance of the custom error type via type - // assertion. - _, e := f2(42) - if ae, ok := e.(*argError); ok { - fmt.Println(ae.arg) - fmt.Println(ae.prob) - } } diff --git a/examples/errors/errors.hash b/examples/errors/errors.hash index a1f74a51c..e368e59fc 100644 --- a/examples/errors/errors.hash +++ b/examples/errors/errors.hash @@ -1,2 +1,2 @@ -00affa44cc98f14c2b10934a4187c9445fac0fe6 -NiJOpCPO3L0 +bd2a75026d7959adf8e633c6084343740dfe842e +o2xrWWk_1MU diff --git a/examples/errors/errors.sh b/examples/errors/errors.sh index 54315ae7d..959a9c120 100644 --- a/examples/errors/errors.sh +++ b/examples/errors/errors.sh @@ -1,10 +1,6 @@ $ go run errors.go -f1 worked: 10 -f1 failed: can't work with 42 -f2 worked: 10 -f2 failed: 42 - can't work with it -42 -can't work with it +f worked: 10 +f failed: can't work with 42 # See this [great post](https://go.dev/blog/error-handling-and-go) # on the Go blog for more on error handling. diff --git a/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.go b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.go new file mode 100644 index 000000000..6315534c8 --- /dev/null +++ b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.go @@ -0,0 +1,51 @@ +// Wrapping errors is a technique that allows you to add additional context +// to an error while preserving the original error. +// This approach is beneficial for debugging and understanding +// the chain of events that led to an error, especially in +// complex applications with multiple layers of function calls. + +package main + +import ( + "errors" + "fmt" + "math/rand/v2" +) + +// A "sentinel error" has usualy the prefix "Err" +var ErrOutOfTea = errors.New("no more tea available.") + +var ErrPower = errors.New("can't boil water.") + +func MakeTea() error { + if rand.Int32N(4) == 0 { + return ErrOutOfTea + } + if rand.Int32N(7) == 0 { + // You can wrap a sentinel error with %w + return fmt.Errorf("Add custom text: %w", ErrPower) + } + return nil +} + +func main() { + for range 14 { + err := MakeTea() + if err != nil { + // By using several if-statements we can handle + // different sentinel errors. + // A switch statement is not applicable here. + if errors.Is(err, ErrOutOfTea) { + fmt.Println("We should buy new tea!") + continue + } + if errors.Is(err, ErrPower) { + fmt.Println("Now it is dark.") + continue + } + fmt.Printf("Some unknown error: %s", err) + continue + } + fmt.Println("The tea is warm and inviting.") + } +} diff --git a/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.hash b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.hash new file mode 100644 index 000000000..230cbf85d --- /dev/null +++ b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.hash @@ -0,0 +1,2 @@ +936b77def6dea78dd79c8eeb59c74c8cd1caf642 +W9gasLW-MJp diff --git a/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.sh b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.sh new file mode 100644 index 000000000..19c75a8a6 --- /dev/null +++ b/examples/wrapping-and-sentinel-errors/wrapping-and-sentinel-errors.sh @@ -0,0 +1,15 @@ +$ go run wrapping-and-sentinel-errors.go +The tea is warm and inviting. +The tea is warm and inviting. +The tea is warm and inviting. +The tea is warm and inviting. +We should buy new tea! +The tea is warm and inviting. +The tea is warm and inviting. +The tea is warm and inviting. +Now it is dark. +The tea is warm and inviting. +The tea is warm and inviting. +The tea is warm and inviting. +We should buy new tea! +The tea is warm and inviting. diff --git a/public/custom-errors b/public/custom-errors new file mode 100644 index 000000000..da217e8c4 --- /dev/null +++ b/public/custom-errors @@ -0,0 +1,206 @@ + + +
+ ++ + | +
+
+
+ |
+
+ Next example: Goroutines. +
+ + + + +package main
error
, a built-in interface.
func f1(arg int) (int, error) {
+ func f(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
- }
+ return -1, errors.New("can't work with 42")
+ }
It’s possible to use custom types as error
s by
-implementing the Error()
method on them. Here’s a
-variant on the example above that uses a custom type
-to explicitly represent an argument error.
type argError struct {
- arg int
- prob string
-}
- func (e *argError) Error() string {
- return fmt.Sprintf("%d - %s", e.arg, e.prob)
-}
- func f2(arg int) (int, error) {
- if arg == 42 {
- In this case we use &argError
syntax to build
-a new struct, supplying values for the two
-fields arg
and prob
.
return -1, &argError{arg, "can't work with it"}
- }
- return arg + 3, nil
-}
- func main() {
- The two loops below test out each of our @@ -190,37 +115,22 @@ idiom in Go code.
for _, i := range []int{7, 42} {
- if r, e := f1(i); e != nil {
- fmt.Println("f1 failed:", e)
- } else {
- fmt.Println("f1 worked:", r)
- }
- }
- for _, i := range []int{7, 42} {
- if r, e := f2(i); e != nil {
- fmt.Println("f2 failed:", e)
- } else {
- fmt.Println("f2 worked:", r)
- }
- }
+ func main() {
If you want to programmatically use the data in -a custom error, you’ll need to get the error as an -instance of the custom error type via type -assertion.
- + _, e := f2(42)
- if ae, ok := e.(*argError); ok {
- fmt.Println(ae.arg)
- fmt.Println(ae.prob)
+ for _, i := range []int{7, 42} {
+ if r, e := f(i); e != nil {
+ fmt.Println("f failed:", e)
+ } else {
+ fmt.Println("f worked:", r)
+ }
}
}
$ go run errors.go
-f1 worked: 10
-f1 failed: can't work with 42
-f2 worked: 10
-f2 failed: 42 - can't work with it
-42
-can't work with it
+f worked: 10
+f failed: can't work with 42
- Next example: Goroutines. + Next example: Wrapping and Sentinel Errors.
@@ -273,7 +179,7 @@ on the Go blog for more on error handling. ++ + | +
+
+
+ |
+
+ Next example: Custom Errors. +
+ + + + +diff --git a/public/goroutines b/public/goroutines index 6b16ea9d9..b9784c738 100644 --- a/public/goroutines +++ b/public/goroutines @@ -9,7 +9,7 @@ onkeydown = (e) => { if (e.key == "ArrowLeft") { - window.location.href = 'errors'; + window.location.href = 'custom-errors'; } diff --git a/public/index.html b/public/index.html index 54853fec8..2daeb4ec3 100644 --- a/public/index.html +++ b/public/index.html @@ -75,6 +75,10 @@
+
+ +
+
diff --git a/public/wrapping-and-sentinel-errors b/public/wrapping-and-sentinel-errors new file mode 100644 index 000000000..4851e6132 --- /dev/null +++ b/public/wrapping-and-sentinel-errors @@ -0,0 +1,204 @@ + + +
+ +
+ + + +
+