Skip to content

Commit e0a85c4

Browse files
committed
Generics talk
1 parent 13cfdac commit e0a85c4

7 files changed

Lines changed: 294 additions & 0 deletions
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# The Globus BB Gophers Meetup
2+
3+
Cyrill Schumacher
4+
Magazine zum Globus AG
5+
6+
## Welcome
7+
8+
Thank you for joining!
9+
10+
.image 2017-08-10/GoCommunity.png _ 700
11+
.caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]
12+
.caption _Graphics_ by [[https://github.com/ashleymcnamara][Ashley McNamara]]
13+
14+
## Let's go!
15+
16+
.image 2017-08-10/Unicorn_Gopher.png _ 400
17+
.caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]
18+
.caption _Graphics_ by [[https://github.com/ashleymcnamara][Ashley McNamara]]
19+
20+
## Go 1.18 main new features
21+
22+
- new support for generic code using parameterized types.
23+
- adds built-in support for writing fuzzing-based tests, to automatically find
24+
inputs that cause your program to crash or return invalid answers.
25+
- adds a new “Go workspace mode”, which lets you work with multiple Go modules
26+
simultaneously, an important use case for larger projects.
27+
- contains an expanded go version -m command, which now records build details
28+
such as compiler flags. A program can query its own build details using
29+
debug.ReadBuildInfo, and it can now read build details from other binaries using
30+
the new debug/buildinfo package. This functionality is meant to be the foundation
31+
for any tool that needs to produce a software bill of materials (SBOM) for Go
32+
binaries.
33+
34+
## Fuzzing
35+
36+
.link https://go.dev/blog/fuzz-beta
37+
Fuzzing is a type of automated testing which continuously manipulates inputs to a
38+
program to find issues such as panics or bugs. These semi-random data mutations
39+
can discover new code coverage that existing unit tests may miss, and uncover
40+
edge case bugs which would otherwise go unnoticed.
41+
42+
.code 2021-02-04/parse_fuzz.go
43+
44+
45+
## Workspace mode
46+
47+
.link https://go.googlesource.com/proposal/+/master/design/45713-workspace.md
48+
A new workspace mode in the go command for editing multiple modules has been
49+
added. The presence of a go.work file in the working directory or a containing
50+
directory will put the go command into workspace mode. The go.work file specifies
51+
a set of local modules that comprise a workspace. When invoked in workspace mode,
52+
the go command will always select these modules and a consistent set of
53+
dependencies.
54+
55+
## Parameterized types
56+
57+
.link https://go.dev/doc/tutorial/generics
58+
59+
Generics are the most significant change to Go since the release of Go 1, and
60+
certainly the largest single language change the Go team have ever made.
61+
62+
## Generics gotchas "return"
63+
64+
.play -edit -numbers 2021-02-04/generic_gotcha_return.go
65+
66+
## Generics gotchas "fmt" 1/2
67+
68+
.play -edit -numbers 2021-02-04/generic_gotcha_fmt.go
69+
70+
## Generics gotchas "fmt" 2/2
71+
72+
the go vet error results in:
73+
74+
$ go vet generic_gotcha_fmt.go
75+
./generic_gotcha_fmt.go:12:20: fmt.Errorf format %q has arg key of wrong type K
76+
77+
Solution is...
78+
79+
## Generics Interface with type constraints (Problem)
80+
81+
.play -numbers 2021-02-04/generic_gotcha_interface_type_constraint01.go
82+
83+
## Generics Interface with type constraints (Solution)
84+
85+
.play -numbers 2021-02-04/generic_gotcha_interface_type_constraint02.go
86+
87+
## Unusual Generics
88+
89+
// Ptr is a simple function that helps to get literal pointer in one-line.
90+
func Ptr[T any](v T) *T { return &v }
91+
92+
Invoked as:
93+
94+
pInt := unusual_generics.Ptr(10)
95+
pStr := unusual_generics.Ptr("test")
96+
pUint64 := unusual_generics.Ptr[uint64](24)
97+
fmt.Printf("%[1]T %[1]d\n", *pInt)
98+
fmt.Printf("%[1]T %[1]s\n", *pStr)
99+
fmt.Printf("%[1]T %[1]d\n", *pUint64)
100+
// Output:
101+
// int 10
102+
// string test
103+
// uint64 24
104+
105+
Attention: future version Go can call directly `&10` or `&"test"`.
106+
107+
## Generic constraints for checks during compile time
108+
109+
.play -edit -numbers 2021-02-04/generic_avoidnil.go
110+
111+
## Generics: bad patterns
112+
113+
pointing out that:
114+
```
115+
func Something[R io.Reader](r R) error { ... }
116+
```
117+
is a useless use of generics and that is correctly written as:
118+
```
119+
func Something(r io.Reader) error { ... }
120+
```
121+
122+
## When to use Generics?
123+
124+
Use them wisely where multiple types occur and not just one.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
// https://github.com/golang/go/issues/45346
9+
// Section: Combining embedded non-interfaces with methods
10+
type TimeLayoutProvider interface {
11+
~struct{} // constrain base type to avoid runtime errors on zero-value method calls
12+
TimeLayout() string
13+
}
14+
15+
type TimeFormatted[T TimeLayoutProvider] time.Time
16+
17+
func (tf TimeFormatted[T]) MarshalText() ([]byte, error) {
18+
var p T
19+
t := time.Time(tf)
20+
return []byte(t.Format(p.TimeLayout())), nil
21+
}
22+
23+
type formatXYZProvider struct{}
24+
25+
func (formatXYZProvider) TimeLayout() string { return "2006 Jan _2 15:04:05 -0700" }
26+
27+
func main() {
28+
now := time.Now()
29+
//layout := TimeFormatted[formatXYZProvider](now)
30+
tf := TimeFormatted[formatXYZProvider](now)
31+
raw, _ := tf.MarshalText()
32+
fmt.Println(string(raw))
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package main
2+
3+
import "fmt"
4+
5+
type FooBar[K comparable, V any] struct {
6+
cache map[K]V
7+
}
8+
9+
func (fb FooBar[K, V]) getKey(key K) (emptyVal V, _ error) {
10+
val, ok := fb.cache[key]
11+
if !ok {
12+
return emptyVal, fmt.Errorf("key %q not found", key)
13+
}
14+
return val, nil
15+
}
16+
func main() {
17+
fb := FooBar[int, []byte]{cache: make(map[int][]byte)} // OMIT
18+
_, err := fb.getKey(3) // OMIT
19+
fmt.Println(err.Error()) // OMIT
20+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
//OMIT
4+
import "fmt"
5+
6+
//OMIT
7+
func main() {
8+
c := Car{Name: "Ferrari"}
9+
Move(c, 100, 20)
10+
}
11+
12+
//OMIT
13+
// Subtractable is a type constraint that defines subtractable datatypes to be
14+
// used in generic functions.
15+
type Subtractable interface {
16+
int | int32 | int64 | float32 | float64 | uint | uint32 | uint64
17+
}
18+
19+
//OMIT
20+
// Moveable is a interface that is used to handle many objects that are moveable
21+
type Moveable interface {
22+
Move(Subtractable)
23+
}
24+
25+
//OMIT
26+
// Car is a test struct for cars, implements Moveable
27+
type Car struct{ Name string }
28+
29+
//OMIT
30+
func (c Car) Move(meters Subtractable) { fmt.Printf("%s moved %d meters\n", c.Name, meters) }
31+
32+
//OMIT
33+
// Move is a generic function that takes in a Moveable and moves it
34+
func Move[V Moveable, D Subtractable](v V, distance D, meters D) D {
35+
v.Move(meters)
36+
return distance - meters
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
//OMIT
4+
import "fmt"
5+
6+
//OMIT
7+
func main() {
8+
c := Car[int]{Name: "Ferrari"}
9+
Move(c, 100, 20)
10+
}
11+
12+
//OMIT
13+
// Subtractable is a type constraint that defines subtractable datatypes to be
14+
// used in generic functions.
15+
type Subtractable interface {
16+
~int | ~int32 | ~int64 | float32 | float64 | uint | uint32 | uint64
17+
}
18+
19+
//OMIT
20+
// Moveable is a interface that is used to handle many objects that are moveable
21+
type Moveable[S Subtractable] interface {
22+
Move(S)
23+
}
24+
25+
//OMIT
26+
// Car is a test struct for cars, implements Moveable
27+
type Car[S Subtractable] struct{ Name string }
28+
29+
//OMIT
30+
func (c Car[S]) Move(meters S) { fmt.Printf("%s moved %v meters\n", c.Name, meters) }
31+
32+
//OMIT
33+
// Move is a generic function that takes in a Moveable and moves it
34+
func Move[D Subtractable, V Moveable[D]](v V, distance D, meters D) D {
35+
v.Move(meters)
36+
return distance - meters
37+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
)
6+
7+
type FooBar2[K comparable, V any] struct {
8+
cache map[K]V
9+
}
10+
11+
func (fb FooBar2[K, V]) getKey(key K) (V, error) {
12+
val, ok := fb.cache[key]
13+
if !ok {
14+
return nil, errors.New("key not found")
15+
}
16+
return val, nil
17+
}
18+
func main() {}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main // OMIT
2+
3+
import ( // OMIT
4+
"net/url" // OMIT
5+
"reflect" // OMIT
6+
"testing" // OMIT
7+
) // OMIT
8+
// OMIT
9+
func FuzzParseQuery(f *testing.F) {
10+
f.Add("x=1&y=2")
11+
f.Fuzz(func(t *testing.T, queryStr string) {
12+
query, err := url.ParseQuery(queryStr)
13+
if err != nil {
14+
t.Skip()
15+
}
16+
queryStr2 := query.Encode()
17+
query2, err := url.ParseQuery(queryStr2)
18+
if err != nil {
19+
t.Fatalf("ParseQuery failed to decode a valid encoded query %s: %v", queryStr2, err)
20+
}
21+
if !reflect.DeepEqual(query, query2) {
22+
t.Errorf("ParseQuery gave different query after being encoded\nbefore: %v\nafter: %v", query, query2)
23+
}
24+
})
25+
}

0 commit comments

Comments
 (0)