-
Notifications
You must be signed in to change notification settings - Fork 168
/
error_3.go
105 lines (89 loc) · 3.91 KB
/
error_3.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// ---------------
// Type as context
// ---------------
// It is not always possible to be able to say the interface value itself will be enough context.
// Sometimes, it requires more context. For example, a networking problem can be really
// complicated. Error variables wouldn't work there.
// Only when the error variables wouldn't work, we should go ahead and start working with
// custom concrete type for the error.
// Below are two custom error types from the json package in the standard library and see how we
// can use those. This is type as context.
// http://golang.org/src/pkg/encoding/json/decode.go
package main
import (
"fmt"
"reflect"
)
// An UnmarshalTypeError describes a JSON value that was not appropriate for
// a value of a specific Go type.
// Naming convention: The word "Error" ends at the name of the type.
type UnmarshalTypeError struct {
Value string // description of JSON value
Type reflect.Type // type of Go value it could not be assigned to
}
// UnmarshalTypeError implements the error interface.
// We are using pointer semantic.
// In the implementation, we are validating all the fields are being used in the error message. If
// not, we have a problem. Because why would you add a field to the custom error type and not
// displaying on your log when this method would call. We only do this when we really need it.
func (e *UnmarshalTypeError) Error() string {
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
// This concrete type is used when we don't pass the address of a value into Unmarshal function.
type InvalidUnmarshalError struct {
Type reflect.Type
}
// InvalidUnmarshalError implements the error interface.
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "json: Unmarshal(nil)"
}
if e.Type.Kind() != reflect.Ptr {
return "json: Unmarshal(non-pointer " + e.Type.String() + ")"
}
return "json: Unmarshal(nil " + e.Type.String() + ")"
}
// user is a type for use in the Unmarshal call.
type user struct {
Name int
}
func main() {
var u user
err := Unmarshal([]byte(`{"name":"bill"}`), u) // Run with a value and pointer.
if err != nil {
// This is a special type assertion that only works on the switch.
switch e := err.(type) {
case *UnmarshalTypeError:
fmt.Printf("UnmarshalTypeError: Value[%s] Type[%v]\n", e.Value, e.Type)
case *InvalidUnmarshalError:
fmt.Printf("InvalidUnmarshalError: Type[%v]\n", e.Type)
default:
fmt.Println(err)
}
return
}
fmt.Println("Name:", u.Name)
}
// Unmarshal simulates an unmarshal call that always fails.
// Notice the parameters here: The first one is a slice of byte and the second one is an empty
// interface. The empty interface basically says nothing, which means any value can be passed into
// this function.
// We are going to reflect on the concrete type that is stored inside this interface and we are
// going to validate that if it is a pointer or not nil. We then return different error types
// depending on these.
func Unmarshal(data []byte, v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(v)}
}
return &UnmarshalTypeError{"string", reflect.TypeOf(v)}
}
// There is one flaw when using type as context here. In this case, we are now going back to the
// concrete. We walk away from the decoupling because our code is now bounded to these concrete
// types. If the developer who wrote the json package makes any changes to these concrete types,
// that's gonna create a cascading effect all the way through our code. We are no longer protected
// by the decoupling of the error interface.
// This sometime has to happen. Can we do something different not to lose the decoupling. This is
// where the idea of behavior as context comes in.