-
Notifications
You must be signed in to change notification settings - Fork 0
/
class three.slide
191 lines (137 loc) · 4.89 KB
/
class three.slide
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
Let's Go
Class #3
Andrew Lader
* So, Let's Go!
*Agenda*
- Go Best Practices & Idiomatic Practices
- Examine the Homework
- New Homework Assignment
* Best Practices
- There are coding best practices, and then there are language best practices
- Examples of coding best practices are patterns, or SOLID principles (read: Clean Code, the book) that can be applied to any language
- Language best practices evolve from the idionsyncrasies of the language itself
.image https://golang.org/doc/gopher/fiveyears.jpg _ 500
* Error Handling
- Go doesn't do exceptions, and a *panic* is fatal, so what's the best way to handle errors?
- Go's functions can return multiple values, which makes error handling easy
- Functions can return their result *and* an error, if there was one
Thus...
func myFunction(arg1 string, arg2 int) (int, error) {
actualValue, err := someOtherFunc(arg1, arg2)
if (err != nil) {
// handle the error here and return, which stops executing this function further
return nil, err
}
// if no errors, then keep working...
return theValue, nil
}
* Error Handling (continued)
- Avoid nested error handling
// don't do this
func doThis(arg1 int) {
result, err := anotherFunc(arg1)
if err == nil {
anotherResult, err := someOtherFunc(arg1, result)
if err == nil {
finalResult, err := finalFunc(arg1, anotherResult)
}
return
}
return
}
* Proper Error Handling
// do this instead
func doThis(arg1 int) {
result, err := anotherFunc(arg1)
if err != nil {
return
}
anotherResult, err := someOtherFunc(arg1, result)
if err != nil {
return
}
finalResult, err := finalFunc(arg1, anotherResult)
return
}
* Readability Conventions
- When using the error value immediately, use the variable name `err`
- Specific errors used later in the code should have full variable names, like `specificError`
- Comments should have a spaces afte the slashes
// do this
//not this
- Variable and functions names should be meaningful; shortening them can lead to confusion
- Variables should be nouns, and functions should begin with verbs
func calculateStateTax(stateName string) { // good
}
func calcStTx(stNm string) { // bad, misleading
}
* More Readability Conventions
- With constructors that have many parameters, pass in a single config struct
- Best option is to pass inline config struct
newObj, err := newObject(objConfig {
Threshold: threshold,
SomeValue: someValue,
Timeout: timeout,
})
if err != nil {
log.Fatal(err)
}
* Using Defer
- There is a cost to using the defer statement, which can affect fast running functions (order of nanoseconds)
- Use it when it improves readability
func doConcurrentWork(locker *sync.Mutex) {
locker.Lock()
defer locker.Unlock()
// rest of the code...
}
* Using Interfaces
- Interfaces are implemented implicitly (non-declared)
- Provide polymorphism by implementing the mehtods defined in interfaces
- Can contain other interfaces
type I1 interface { // methods i1(), i2(), fmt.Stringer()
i1()
I2
fmt.Stringer
}
type I2 interface {
i2()
}
* Empty Interface
- Go uses `interface{}` to represent any type
- This can be used to create functions that accept any value
func printAnyValue(someValue interface{}) {
fmt.Printf("%v, %T", someValue, someValue)
}
* Handling Overloading of Types in Go
- Use type switching to handle overloading of different types
func fooBar(inputValue interface{}) {
switch inputValue.(type) {
case string:
stringValue := inputValue.(string)
fmt.Printf("String value: %s", stringValue)
case int:
intValue := inputValue.(int)
fmt.Printf("Integer value: %d", intValue)
}
}
func main() {
intValue := 32
stringValue := "Hello"
fooBar(intValue)
fooBar(stringValue)
}
[[https://play.golang.org/p/OYX_A2nPTJ][Playground Example]]
* Empty Structs
- What? Why?
- Well, they have no width (size)
- Useful for signaling channels or iterating over lists
- can even have methods
[[https://play.golang.org/p/LEzP9FqUBu][Example Playground]]
* Homework
.caption Your mission -- _should_you_choose_to_accept_it_ -- _and_you_will!_
- Build a web service that returns the weather by zipcode
- Manage an in-memory cache of zipcodes-to-weather
- User worker pools to manage large number of incoming requests
* Resources
- [[https://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right][Why Go gets exceptions right]]
- [[https://dave.cheney.net/2014/03/25/the-empty-struct][Empty Struct]]