mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-21 00:47:11 +08:00
All
This commit is contained in:
parent
8e2f2cb83d
commit
b37e4abb2a
154 changed files with 5466 additions and 1000362 deletions
23
10-standard-lib/75-wrong-time-duration/main.go
Normal file
23
10-standard-lib/75-wrong-time-duration/main.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
func listing1() {
|
||||
ticker := time.NewTicker(1000)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Do something
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
ticker := time.NewTicker(time.Microsecond)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Do something
|
||||
}
|
||||
}
|
||||
}
|
||||
51
10-standard-lib/76-time-after/main.go
Normal file
51
10-standard-lib/76-time-after/main.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func consumer1(ch <-chan Event) {
|
||||
for {
|
||||
select {
|
||||
case event := <-ch:
|
||||
handle(event)
|
||||
case <-time.After(time.Hour):
|
||||
log.Println("warning: no messages received")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func consumer2(ch <-chan Event) {
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
|
||||
select {
|
||||
case event := <-ch:
|
||||
cancel()
|
||||
handle(event)
|
||||
case <-ctx.Done():
|
||||
log.Println("warning: no messages received")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func consumer3(ch <-chan Event) {
|
||||
timerDuration := 1 * time.Hour
|
||||
timer := time.NewTimer(timerDuration)
|
||||
|
||||
for {
|
||||
timer.Reset(timerDuration)
|
||||
select {
|
||||
case event := <-ch:
|
||||
handle(event)
|
||||
case <-timer.C:
|
||||
log.Println("warning: no messages received")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Event struct{}
|
||||
|
||||
func handle(Event) {
|
||||
}
|
||||
17
10-standard-lib/77-json-handling/map-any/main.go
Normal file
17
10-standard-lib/77-json-handling/map-any/main.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func listing1() error {
|
||||
b := getMessage()
|
||||
var m map[string]any
|
||||
err := json.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMessage() []byte {
|
||||
return nil
|
||||
}
|
||||
53
10-standard-lib/77-json-handling/monotonic-clock/main.go
Normal file
53
10-standard-lib/77-json-handling/monotonic-clock/main.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func listing1() error {
|
||||
t := time.Now()
|
||||
event1 := Event{
|
||||
Time: t,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(event1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var event2 Event
|
||||
err = json.Unmarshal(b, &event2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(event1 == event2)
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing2() error {
|
||||
t := time.Now()
|
||||
event1 := Event{
|
||||
Time: t.Truncate(0),
|
||||
}
|
||||
|
||||
b, err := json.Marshal(event1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var event2 Event
|
||||
err = json.Unmarshal(b, &event2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(event1 == event2)
|
||||
return nil
|
||||
}
|
||||
56
10-standard-lib/77-json-handling/type-embedding/main.go
Normal file
56
10-standard-lib/77-json-handling/type-embedding/main.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := listing1(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := listing2(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Event1 struct {
|
||||
ID int
|
||||
time.Time
|
||||
}
|
||||
|
||||
func listing1() error {
|
||||
event := Event1{
|
||||
ID: 1234,
|
||||
Time: time.Now(),
|
||||
}
|
||||
|
||||
b, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
type Event2 struct {
|
||||
ID int
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func listing2() error {
|
||||
event := Event2{
|
||||
ID: 1234,
|
||||
Time: time.Now(),
|
||||
}
|
||||
|
||||
b, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
66
10-standard-lib/78-sql/null-values/main.go
Normal file
66
10-standard-lib/78-sql/null-values/main.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package main
|
||||
|
||||
import "database/sql"
|
||||
|
||||
func listing1(db *sql.DB, id string) error {
|
||||
rows, err := db.Query("SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Defer closing rows
|
||||
|
||||
var (
|
||||
department string
|
||||
age int
|
||||
)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&department, &age)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ...
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing2(db *sql.DB, id string) error {
|
||||
rows, err := db.Query("SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Defer closing rows
|
||||
|
||||
var (
|
||||
department *string
|
||||
age int
|
||||
)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&department, &age)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ...
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing3(db *sql.DB, id string) error {
|
||||
rows, err := db.Query("SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Defer closing rows
|
||||
|
||||
var (
|
||||
department sql.NullString
|
||||
age int
|
||||
)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&department, &age)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ...
|
||||
}
|
||||
return nil
|
||||
}
|
||||
16
10-standard-lib/78-sql/prepared-statements/main.go
Normal file
16
10-standard-lib/78-sql/prepared-statements/main.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import "database/sql"
|
||||
|
||||
func listing1(db *sql.DB, id string) error {
|
||||
stmt, err := db.Prepare("SELECT * FROM ORDER WHERE ID = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := stmt.Query(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = rows
|
||||
return nil
|
||||
}
|
||||
68
10-standard-lib/78-sql/rows-iterations-errors/main.go
Normal file
68
10-standard-lib/78-sql/rows-iterations-errors/main.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log"
|
||||
)
|
||||
|
||||
func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
|
||||
rows, err := c.db.QueryContext(ctx,
|
||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer func() {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
log.Printf("failed to close rows: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
department string
|
||||
age int
|
||||
)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&department, &age)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return department, age, nil
|
||||
}
|
||||
|
||||
func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
|
||||
rows, err := c.db.QueryContext(ctx,
|
||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer func() {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
log.Printf("failed to close rows: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
department string
|
||||
age int
|
||||
)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&department, &age)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return department, age, nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
db *sql.DB
|
||||
}
|
||||
19
10-standard-lib/78-sql/sql-open/main.go
Normal file
19
10-standard-lib/78-sql/sql-open/main.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package main
|
||||
|
||||
import "database/sql"
|
||||
|
||||
var dsn = ""
|
||||
|
||||
func listing1() error {
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = db
|
||||
return nil
|
||||
}
|
||||
81
10-standard-lib/79-closing-resources/http/main.go
Normal file
81
10-standard-lib/79-closing-resources/http/main.go
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h handler) getBody1() (string, error) {
|
||||
resp, err := h.client.Get(h.url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (h handler) getBody2() (string, error) {
|
||||
resp, err := h.client.Get(h.url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("failed to close response: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (h handler) getStatusCode1(body io.Reader) (int, error) {
|
||||
resp, err := h.client.Post(h.url, "application/json", body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("failed to close response: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
func (h handler) getStatusCode2(body io.Reader) (int, error) {
|
||||
resp, err := h.client.Post(h.url, "application/json", body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("failed to close response: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
client http.Client
|
||||
url string
|
||||
}
|
||||
56
10-standard-lib/79-closing-resources/os-file/main.go
Normal file
56
10-standard-lib/79-closing-resources/os-file/main.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func listing1(filename string) error {
|
||||
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Printf("failed to close file: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeToFile1(filename string, content []byte) (err error) {
|
||||
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
closeErr := f.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = f.Write(content)
|
||||
return
|
||||
}
|
||||
|
||||
func writeToFile2(filename string, content []byte) (err error) {
|
||||
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.Write(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.Sync()
|
||||
}
|
||||
48
10-standard-lib/79-closing-resources/sql-rows/main.go
Normal file
48
10-standard-lib/79-closing-resources/sql-rows/main.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
)
|
||||
|
||||
func listing1() error {
|
||||
db, err := sql.Open("postgres", dataSourceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT * FROM CUSTOMERS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use rows
|
||||
_ = rows
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing2() error {
|
||||
db, err := sql.Open("postgres", dataSourceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT * FROM CUSTOMERS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
log.Printf("failed to close rows: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Use rows
|
||||
_ = rows
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var dataSourceName = ""
|
||||
28
10-standard-lib/80-http-return/main.go
Normal file
28
10-standard-lib/80-http-return/main.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import "net/http"
|
||||
|
||||
func handler1(w http.ResponseWriter, req *http.Request) {
|
||||
err := foo(req)
|
||||
if err != nil {
|
||||
http.Error(w, "foo", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte("all good"))
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func handler2(w http.ResponseWriter, req *http.Request) {
|
||||
err := foo(req)
|
||||
if err != nil {
|
||||
http.Error(w, "foo", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte("all good"))
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func foo(req *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
21
10-standard-lib/81-default-http-client-server/client/main.go
Normal file
21
10-standard-lib/81-default-http-client-server/client/main.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: time.Second,
|
||||
ResponseHeaderTimeout: time.Second,
|
||||
},
|
||||
}
|
||||
_ = client
|
||||
}
|
||||
20
10-standard-lib/81-default-http-client-server/server/main.go
Normal file
20
10-standard-lib/81-default-http-client-server/server/main.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := &http.Server{
|
||||
Addr: ":8080",
|
||||
ReadHeaderTimeout: 500 * time.Millisecond,
|
||||
ReadTimeout: 500 * time.Millisecond,
|
||||
Handler: http.TimeoutHandler(handler{}, time.Second, "foo"),
|
||||
}
|
||||
_ = s
|
||||
}
|
||||
|
||||
type handler struct{}
|
||||
|
||||
func (h handler) ServeHTTP(http.ResponseWriter, *http.Request) {}
|
||||
19
11-testing/82-categorizing-tests/build-tags/db_test.go
Normal file
19
11-testing/82-categorizing-tests/build-tags/db_test.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// +build integration
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInsert1(t *testing.T) {
|
||||
// ...
|
||||
}
|
||||
|
||||
func TestInsert2(t *testing.T) {
|
||||
if os.Getenv("INTEGRATION") != "true" {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
10
11-testing/82-categorizing-tests/short-mode/main_test.go
Normal file
10
11-testing/82-categorizing-tests/short-mode/main_test.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLongRunning(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping long-running test")
|
||||
}
|
||||
// ...
|
||||
}
|
||||
16
11-testing/85-table-driven-tests/main.go
Normal file
16
11-testing/85-table-driven-tests/main.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
import "strings"
|
||||
|
||||
func removeNewLineSuffixes(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if strings.HasSuffix(s, "\r\n") {
|
||||
return removeNewLineSuffixes(s[:len(s)-2])
|
||||
}
|
||||
if strings.HasSuffix(s, "\n") {
|
||||
return removeNewLineSuffixes(s[:len(s)-1])
|
||||
}
|
||||
return s
|
||||
}
|
||||
81
11-testing/85-table-driven-tests/main_test.go
Normal file
81
11-testing/85-table-driven-tests/main_test.go
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRemoveNewLineSuffix_Empty(t *testing.T) {
|
||||
got := removeNewLineSuffixes("")
|
||||
expected := ""
|
||||
if got != expected {
|
||||
t.Errorf("got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNewLineSuffix_EndingWithCarriageReturnNewLine(t *testing.T) {
|
||||
got := removeNewLineSuffixes("a\r\n")
|
||||
expected := "a"
|
||||
if got != expected {
|
||||
t.Errorf("got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNewLineSuffix_EndingWithNewLine(t *testing.T) {
|
||||
got := removeNewLineSuffixes("a\n")
|
||||
expected := "a"
|
||||
if got != expected {
|
||||
t.Errorf("got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNewLineSuffix_EndingWithMultipleNewLines(t *testing.T) {
|
||||
got := removeNewLineSuffixes("a\n\n\n")
|
||||
expected := "a"
|
||||
if got != expected {
|
||||
t.Errorf("got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNewLineSuffix_EndingWithoutNewLine(t *testing.T) {
|
||||
got := removeNewLineSuffixes("a\n")
|
||||
expected := "a"
|
||||
if got != expected {
|
||||
t.Errorf("got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNewLineSuffix(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
`empty`: {
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
`ending with \r\n`: {
|
||||
input: "a\r\n",
|
||||
expected: "a",
|
||||
},
|
||||
`ending with \n`: {
|
||||
input: "a\n",
|
||||
expected: "a",
|
||||
},
|
||||
`ending with multiple \n`: {
|
||||
input: "a\n\n\n",
|
||||
expected: "a",
|
||||
},
|
||||
`ending without newline`: {
|
||||
input: "a",
|
||||
expected: "a",
|
||||
},
|
||||
}
|
||||
for name, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := removeNewLineSuffixes(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("got: %s, expected: %s", got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
30
11-testing/86-sleeping/main.go
Normal file
30
11-testing/86-sleeping/main.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
type Handler struct {
|
||||
n int
|
||||
publisher publisher
|
||||
}
|
||||
|
||||
type publisher interface {
|
||||
Publish([]Foo)
|
||||
}
|
||||
|
||||
func (h Handler) getBestFoo(someInputs int) Foo {
|
||||
foos := getFoos(someInputs)
|
||||
best := foos[0]
|
||||
|
||||
go func() {
|
||||
if len(foos) > h.n {
|
||||
foos = foos[:h.n]
|
||||
}
|
||||
h.publisher.Publish(foos)
|
||||
}()
|
||||
|
||||
return best
|
||||
}
|
||||
|
||||
func getFoos(inputs int) []Foo {
|
||||
return make([]Foo, 100)
|
||||
}
|
||||
|
||||
type Foo struct{}
|
||||
79
11-testing/86-sleeping/main_test.go
Normal file
79
11-testing/86-sleeping/main_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type publisherMock1 struct {
|
||||
mu sync.RWMutex
|
||||
got []Foo
|
||||
}
|
||||
|
||||
func (p *publisherMock1) Publish(got []Foo) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.got = got
|
||||
}
|
||||
|
||||
func (p *publisherMock1) Get() []Foo {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.got
|
||||
}
|
||||
|
||||
func TestGetBestFoo(t *testing.T) {
|
||||
mock := publisherMock1{}
|
||||
h := Handler{
|
||||
publisher: &mock,
|
||||
n: 2,
|
||||
}
|
||||
|
||||
foo := h.getBestFoo(42)
|
||||
// Check foo
|
||||
_ = foo
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
published := mock.Get()
|
||||
// Check published
|
||||
_ = published
|
||||
}
|
||||
|
||||
func assert(t *testing.T, assertion func() bool,
|
||||
maxRetry int, waitTime time.Duration) {
|
||||
for i := 0; i < maxRetry; i++ {
|
||||
if assertion() {
|
||||
return
|
||||
}
|
||||
time.Sleep(waitTime)
|
||||
}
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
type publisherMock2 struct {
|
||||
ch chan []Foo
|
||||
}
|
||||
|
||||
func (p *publisherMock2) Publish(got []Foo) {
|
||||
p.ch <- got
|
||||
}
|
||||
|
||||
func TestGetBestFoo2(t *testing.T) {
|
||||
mock := publisherMock2{
|
||||
ch: make(chan []Foo),
|
||||
}
|
||||
defer close(mock.ch)
|
||||
|
||||
h := Handler{
|
||||
publisher: &mock,
|
||||
n: 2,
|
||||
}
|
||||
foo := h.getBestFoo(42)
|
||||
// Check foo
|
||||
_ = foo
|
||||
|
||||
if v := len(<-mock.ch); v != 2 {
|
||||
t.Fatalf("expected 2, got %d", v)
|
||||
}
|
||||
}
|
||||
43
11-testing/87-time-api/listing1/main.go
Normal file
43
11-testing/87-time-api/listing1/main.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
events []Event
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Timestamp time.Time
|
||||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
t := time.Now().Add(-since)
|
||||
for i := 0; i < len(c.events); i++ {
|
||||
if c.events[i].Timestamp.After(t) {
|
||||
c.events = c.events[i:]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Add(events []Event) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.events = append(c.events, events...)
|
||||
}
|
||||
|
||||
func (c *Cache) GetAll() []Event {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.events
|
||||
}
|
||||
22
11-testing/87-time-api/listing1/main_test.go
Normal file
22
11-testing/87-time-api/listing1/main_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
|
||||
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
|
||||
{Timestamp: time.Now().Add(10 * time.Millisecond)},
|
||||
}
|
||||
cache := &Cache{}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(15 * time.Millisecond)
|
||||
got := cache.GetAll()
|
||||
expected := 2
|
||||
if len(got) != expected {
|
||||
t.Fatalf("expected %d, got %d", expected, len(got))
|
||||
}
|
||||
}
|
||||
53
11-testing/87-time-api/listing2/main.go
Normal file
53
11-testing/87-time-api/listing2/main.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type now func() time.Time
|
||||
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
events []Event
|
||||
now now
|
||||
}
|
||||
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
events: make([]Event, 0),
|
||||
now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Timestamp time.Time
|
||||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
t := time.Now().Add(-since)
|
||||
for i := 0; i < len(c.events); i++ {
|
||||
if c.events[i].Timestamp.After(t) {
|
||||
c.events = c.events[i:]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Add(events []Event) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.events = append(c.events, events...)
|
||||
}
|
||||
|
||||
func (c *Cache) GetAll() []Event {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.events
|
||||
}
|
||||
28
11-testing/87-time-api/listing2/main_test.go
Normal file
28
11-testing/87-time-api/listing2/main_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.06Z")},
|
||||
}
|
||||
cache := &Cache{now: func() time.Time {
|
||||
return parseTime(t, "2020-01-01T12:00:00.06Z")
|
||||
}}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(15 * time.Millisecond)
|
||||
// ...
|
||||
}
|
||||
|
||||
func parseTime(t *testing.T, timestamp string) time.Time {
|
||||
ts, err := time.Parse(time.RFC3339, timestamp)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
return ts
|
||||
}
|
||||
43
11-testing/87-time-api/listing3/main.go
Normal file
43
11-testing/87-time-api/listing3/main.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
events []Event
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Timestamp time.Time
|
||||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(now time.Time, since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
t := now.Add(-since)
|
||||
for i := 0; i < len(c.events); i++ {
|
||||
if c.events[i].Timestamp.After(t) {
|
||||
c.events = c.events[i:]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Add(events []Event) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.events = append(c.events, events...)
|
||||
}
|
||||
|
||||
func (c *Cache) GetAll() []Event {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.events
|
||||
}
|
||||
30
11-testing/87-time-api/listing3/main_test.go
Normal file
30
11-testing/87-time-api/listing3/main_test.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
|
||||
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
|
||||
{Timestamp: time.Now().Add(10 * time.Millisecond)},
|
||||
}
|
||||
cache := &Cache{}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(parseTime(t, "2020-01-01T12:00:00.06Z"), 15*time.Millisecond)
|
||||
got := cache.GetAll()
|
||||
expected := 2
|
||||
if len(got) != expected {
|
||||
t.Fatalf("expected %d, got %d", expected, len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func parseTime(t *testing.T, timestamp string) time.Time {
|
||||
ts, err := time.Parse(time.RFC3339, timestamp)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
return ts
|
||||
}
|
||||
65
11-testing/88-utility-package/httptest/main.go
Normal file
65
11-testing/88-utility-package/httptest/main.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("X-API-VERSION", "1.0")
|
||||
b, _ := io.ReadAll(r.Body)
|
||||
_, _ = w.Write(append([]byte("hello "), b...))
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func (c DurationClient) GetDuration(url string,
|
||||
lat1, lng1, lat2, lng2 float64) (
|
||||
time.Duration, error) {
|
||||
resp, err := c.client.Post(
|
||||
url, "application/json",
|
||||
buildRequestBody(lat1, lng1, lat2, lng2),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return parseResponseBody(resp.Body)
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Duration int
|
||||
}
|
||||
|
||||
func buildRequestBody(lat1, lng1, lat2, lng2 float64) io.Reader {
|
||||
return strings.NewReader("")
|
||||
}
|
||||
|
||||
type DurationClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewDurationClient() DurationClient {
|
||||
return DurationClient{
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
}
|
||||
|
||||
func parseResponseBody(r io.ReadCloser) (time.Duration, error) {
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = r.Close()
|
||||
}()
|
||||
|
||||
var req request
|
||||
err = json.Unmarshal(b, &req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return time.Duration(req.Duration) * time.Second, nil
|
||||
}
|
||||
52
11-testing/88-utility-package/httptest/main_test.go
Normal file
52
11-testing/88-utility-package/httptest/main_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://localhost",
|
||||
strings.NewReader("foo"))
|
||||
w := httptest.NewRecorder()
|
||||
Handler(w, req)
|
||||
|
||||
if got := w.Result().Header.Get("X-API-VERSION"); got != "1.0" {
|
||||
t.Errorf("api version: expected 1.0, got %s", got)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(w.Body)
|
||||
if got := string(body); got != "hello foo" {
|
||||
t.Errorf("body: expected hello foo, got %s", got)
|
||||
}
|
||||
|
||||
if http.StatusOK != w.Result().StatusCode {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationClientGet(t *testing.T) {
|
||||
srv := httptest.NewServer(
|
||||
http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"duration": 314}`))
|
||||
},
|
||||
),
|
||||
)
|
||||
defer srv.Close()
|
||||
|
||||
client := NewDurationClient()
|
||||
duration, err :=
|
||||
client.GetDuration(srv.URL, 51.551261, -0.1221146, 51.57, -0.13)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if duration != 314*time.Second {
|
||||
t.Errorf("expected 314 seconds, got %v", duration)
|
||||
}
|
||||
}
|
||||
44
11-testing/88-utility-package/iotest/main.go
Normal file
44
11-testing/88-utility-package/iotest/main.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type LowerCaseReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (l LowerCaseReader) Read(p []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func foo(r io.Reader) error {
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ...
|
||||
_ = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAll(r io.Reader, retries int) ([]byte, error) {
|
||||
b := make([]byte, 0, 512)
|
||||
for {
|
||||
if len(b) == cap(b) {
|
||||
b = append(b, 0)[:len(b)]
|
||||
}
|
||||
n, err := r.Read(b[len(b):cap(b)])
|
||||
b = b[:len(b)+n]
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
retries--
|
||||
if retries <= 0 {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
11-testing/88-utility-package/iotest/main_test.go
Normal file
30
11-testing/88-utility-package/iotest/main_test.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/iotest"
|
||||
)
|
||||
|
||||
func TestLowerCaseReader(t *testing.T) {
|
||||
err := iotest.TestReader(
|
||||
&LowerCaseReader{reader: strings.NewReader("aBcDeFgHiJ")},
|
||||
[]byte("acegi"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
err := foo(iotest.TimeoutReader(
|
||||
strings.NewReader(randomString(1024)),
|
||||
))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func randomString(i int) string {
|
||||
return string(make([]byte, i))
|
||||
}
|
||||
15
11-testing/89-benchmark/compiler-optimizations/main.go
Normal file
15
11-testing/89-benchmark/compiler-optimizations/main.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
const (
|
||||
m1 = 0x5555555555555555
|
||||
m2 = 0x3333333333333333
|
||||
m4 = 0x0f0f0f0f0f0f0f0f
|
||||
h01 = 0x0101010101010101
|
||||
)
|
||||
|
||||
func popcnt(x uint64) uint64 {
|
||||
x -= (x >> 1) & m1
|
||||
x = (x & m2) + ((x >> 2) & m2)
|
||||
x = (x + (x >> 4)) & m4
|
||||
return (x * h01) >> 56
|
||||
}
|
||||
19
11-testing/89-benchmark/compiler-optimizations/main_test.go
Normal file
19
11-testing/89-benchmark/compiler-optimizations/main_test.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkPopcnt1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
popcnt(uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
var global uint64
|
||||
|
||||
func BenchmarkPopcnt2(b *testing.B) {
|
||||
var v uint64
|
||||
for i := 0; i < b.N; i++ {
|
||||
v = popcnt(uint64(i))
|
||||
}
|
||||
global = v
|
||||
}
|
||||
21
11-testing/89-benchmark/observer-effect/main.go
Normal file
21
11-testing/89-benchmark/observer-effect/main.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package main
|
||||
|
||||
func calculateSum512(s [][512]int64) int64 {
|
||||
var sum int64
|
||||
for i := 0; i < len(s); i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
sum += s[i][j]
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func calculateSum513(s [][513]int64) int64 {
|
||||
var sum int64
|
||||
for i := 0; i < len(s); i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
sum += s[i][j]
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
57
11-testing/89-benchmark/observer-effect/main_test.go
Normal file
57
11-testing/89-benchmark/observer-effect/main_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
const rows = 1000
|
||||
|
||||
var res int64
|
||||
|
||||
func BenchmarkCalculateSum512_1(b *testing.B) {
|
||||
var sum int64
|
||||
s := createMatrix512(rows)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sum = calculateSum512(s)
|
||||
}
|
||||
res = sum
|
||||
}
|
||||
|
||||
func BenchmarkCalculateSum513_1(b *testing.B) {
|
||||
var sum int64
|
||||
s := createMatrix513(rows)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sum = calculateSum513(s)
|
||||
}
|
||||
res = sum
|
||||
}
|
||||
|
||||
func BenchmarkCalculateSum512_2(b *testing.B) {
|
||||
var sum int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
s := createMatrix512(rows)
|
||||
b.StartTimer()
|
||||
sum = calculateSum512(s)
|
||||
}
|
||||
res = sum
|
||||
}
|
||||
|
||||
func BenchmarkCalculateSum513_2(b *testing.B) {
|
||||
var sum int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
s := createMatrix512(rows)
|
||||
b.StartTimer()
|
||||
sum = calculateSum512(s)
|
||||
}
|
||||
res = sum
|
||||
}
|
||||
|
||||
func createMatrix512(r int) [][512]int64 {
|
||||
return make([][512]int64, r)
|
||||
}
|
||||
|
||||
func createMatrix513(r int) [][513]int64 {
|
||||
return make([][513]int64, r)
|
||||
}
|
||||
26
11-testing/89-benchmark/timer/main_test.go
Normal file
26
11-testing/89-benchmark/timer/main_test.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package timer
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkFoo1(b *testing.B) {
|
||||
expensiveSetup()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
functionUnderTest()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFoo2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
expensiveSetup()
|
||||
b.StartTimer()
|
||||
functionUnderTest()
|
||||
}
|
||||
}
|
||||
|
||||
func functionUnderTest() {
|
||||
}
|
||||
|
||||
func expensiveSetup() {
|
||||
}
|
||||
20
11-testing/89-benchmark/wrong-assumptions/main_test.go
Normal file
20
11-testing/89-benchmark/wrong-assumptions/main_test.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkAtomicStoreInt32(b *testing.B) {
|
||||
var v int32
|
||||
for i := 0; i < b.N; i++ {
|
||||
atomic.StoreInt32(&v, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAtomicStoreInt64(b *testing.B) {
|
||||
var v int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
atomic.StoreInt64(&v, 1)
|
||||
}
|
||||
}
|
||||
10
11-testing/90-testing-features/different-package/main.go
Normal file
10
11-testing/90-testing-features/different-package/main.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package counter
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
var count uint64
|
||||
|
||||
func Inc() uint64 {
|
||||
atomic.AddUint64(&count, 1)
|
||||
return count
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package counter_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
counter "github.com/teivah/100-go-mistakes/11-testing/90-testing-features/different-package"
|
||||
)
|
||||
|
||||
func TestCount(t *testing.T) {
|
||||
if counter.Inc() != 1 {
|
||||
t.Errorf("expected 1")
|
||||
}
|
||||
}
|
||||
37
11-testing/90-testing-features/setup-teardown/main_test.go
Normal file
37
11-testing/90-testing-features/setup-teardown/main_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMySQLIntegration(t *testing.T) {
|
||||
s := setupMySQL()
|
||||
defer teardownMySQL(s)
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
func createConnection(t *testing.T, dsn string) *sql.DB {
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
t.Cleanup(
|
||||
func() {
|
||||
_ = db.Close()
|
||||
})
|
||||
return db
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
s := setupMySQL()
|
||||
code := m.Run()
|
||||
teardownMySQL(s)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func setupMySQL() interface{} { return nil }
|
||||
|
||||
func teardownMySQL(m interface{}) {}
|
||||
22
11-testing/90-testing-features/utility-function/main.go
Normal file
22
11-testing/90-testing-features/utility-function/main.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
func NewStore() Store {
|
||||
return Store{}
|
||||
}
|
||||
|
||||
type Store struct{}
|
||||
|
||||
func (s Store) PutCustomer(Customer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Customer struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func customerFactory(id string) (Customer, error) {
|
||||
if id == "" {
|
||||
return Customer{}, nil
|
||||
}
|
||||
return Customer{id: id}, nil
|
||||
}
|
||||
36
11-testing/90-testing-features/utility-function/main_test.go
Normal file
36
11-testing/90-testing-features/utility-function/main_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPutCustomer1(t *testing.T) {
|
||||
customer, err := createCustomer1()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = NewStore().PutCustomer(customer)
|
||||
// Check err
|
||||
_ = err
|
||||
}
|
||||
|
||||
func createCustomer1() (Customer, error) {
|
||||
customer, err := customerFactory("foo")
|
||||
if err != nil {
|
||||
return Customer{}, nil
|
||||
}
|
||||
return customer, nil
|
||||
}
|
||||
|
||||
func TestPutCustomer(t *testing.T) {
|
||||
customer := createCustomer2(t)
|
||||
err := NewStore().PutCustomer(customer)
|
||||
// Check err
|
||||
_ = err
|
||||
}
|
||||
|
||||
func createCustomer2(t *testing.T) Customer {
|
||||
customer, err := customerFactory("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return customer
|
||||
}
|
||||
17
12-optimizations/91-cpu-caches/cache-line/main.go
Normal file
17
12-optimizations/91-cpu-caches/cache-line/main.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
func sum2(s []int64) int64 {
|
||||
var total int64
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
total += s[i]
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func sum8(s []int64) int64 {
|
||||
var total int64
|
||||
for i := 0; i < len(s); i += 8 {
|
||||
total += s[i]
|
||||
}
|
||||
return total
|
||||
}
|
||||
27
12-optimizations/91-cpu-caches/cache-line/main_test.go
Normal file
27
12-optimizations/91-cpu-caches/cache-line/main_test.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
var global int64
|
||||
|
||||
func BenchmarkSum2(b *testing.B) {
|
||||
var local int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
s := make([]int64, 1_000_000)
|
||||
b.StartTimer()
|
||||
local = sum2(s)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkSum8(b *testing.B) {
|
||||
var local int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
s := make([]int64, 1_000_000)
|
||||
b.StartTimer()
|
||||
local = sum8(s)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
23
12-optimizations/91-cpu-caches/predictability/main.go
Normal file
23
12-optimizations/91-cpu-caches/predictability/main.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
type node struct {
|
||||
value int64
|
||||
next *node
|
||||
}
|
||||
|
||||
func linkedList(n *node) int64 {
|
||||
var total int64
|
||||
for n != nil {
|
||||
total += n.value
|
||||
n = n.next
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func sum2(s []int64) int64 {
|
||||
var total int64
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
total += s[i]
|
||||
}
|
||||
return total
|
||||
}
|
||||
27
12-optimizations/91-cpu-caches/slice-structs/main.go
Normal file
27
12-optimizations/91-cpu-caches/slice-structs/main.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
type Foo struct {
|
||||
a int64
|
||||
b int64
|
||||
}
|
||||
|
||||
func sumFoo(foos []Foo) int64 {
|
||||
var total int64
|
||||
for i := 0; i < len(foos); i++ {
|
||||
total += foos[i].a
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
a []int64
|
||||
b []int64
|
||||
}
|
||||
|
||||
func sumBar(bar Bar) int64 {
|
||||
var total int64
|
||||
for i := 0; i < len(bar.a); i++ {
|
||||
total += bar.a[i]
|
||||
}
|
||||
return total
|
||||
}
|
||||
32
12-optimizations/91-cpu-caches/slice-structs/main_test.go
Normal file
32
12-optimizations/91-cpu-caches/slice-structs/main_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
var global int64
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
func BenchmarkSumFoo(b *testing.B) {
|
||||
var local int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
s := make([]Foo, n)
|
||||
b.StartTimer()
|
||||
local = sumFoo(s)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkSumBar(b *testing.B) {
|
||||
var local int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
bar := Bar{
|
||||
a: make([]int64, n),
|
||||
b: make([]int64, n),
|
||||
}
|
||||
b.StartTimer()
|
||||
local = sumBar(bar)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
67
12-optimizations/92-false-sharing/main.go
Normal file
67
12-optimizations/92-false-sharing/main.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type Input struct {
|
||||
a int64
|
||||
b int64
|
||||
}
|
||||
|
||||
type Result1 struct {
|
||||
sumA int64
|
||||
sumB int64
|
||||
}
|
||||
|
||||
func count1(inputs []Input) Result1 {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
result := Result1{}
|
||||
|
||||
go func() {
|
||||
for i := 0; i < len(inputs); i++ {
|
||||
result.sumA += inputs[i].a
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for i := 0; i < len(inputs); i++ {
|
||||
result.sumB += inputs[i].b
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
return result
|
||||
}
|
||||
|
||||
type Result2 struct {
|
||||
sumA int64
|
||||
_ [56]byte
|
||||
sumB int64
|
||||
}
|
||||
|
||||
func count2(inputs []Input) Result2 {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
result := Result2{}
|
||||
|
||||
go func() {
|
||||
for i := 0; i < len(inputs); i++ {
|
||||
result.sumA += inputs[i].a
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for i := 0; i < len(inputs); i++ {
|
||||
result.sumB += inputs[i].b
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
return result
|
||||
}
|
||||
31
12-optimizations/92-false-sharing/main_test.go
Normal file
31
12-optimizations/92-false-sharing/main_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
var globalResult1 Result1
|
||||
|
||||
func BenchmarkCount1(b *testing.B) {
|
||||
var local Result1
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
inputs := make([]Input, n)
|
||||
b.StartTimer()
|
||||
local = count1(inputs)
|
||||
}
|
||||
globalResult1 = local
|
||||
}
|
||||
|
||||
var globalResult2 Result2
|
||||
|
||||
func BenchmarkCount2(b *testing.B) {
|
||||
var local Result2
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
inputs := make([]Input, n)
|
||||
b.StartTimer()
|
||||
local = count2(inputs)
|
||||
}
|
||||
globalResult2 = local
|
||||
}
|
||||
24
12-optimizations/93-instruction-level-parallelism/main.go
Normal file
24
12-optimizations/93-instruction-level-parallelism/main.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
func add(s [2]int64) [2]int64 {
|
||||
for i := 0; i < n; i++ {
|
||||
s[0]++
|
||||
if s[0]%2 == 0 {
|
||||
s[1]++
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func add2(s [2]int64) [2]int64 {
|
||||
for i := 0; i < n; i++ {
|
||||
v := s[0]
|
||||
s[0] = v + 1
|
||||
if v%2 != 0 {
|
||||
s[1]++
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
var global [2]int64
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
a := [2]int64{}
|
||||
var local [2]int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = add(a)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkAdd2(b *testing.B) {
|
||||
a := [2]int64{}
|
||||
var local [2]int64
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = add2(a)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
29
12-optimizations/94-data-alignment/main.go
Normal file
29
12-optimizations/94-data-alignment/main.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
type Foo1 struct {
|
||||
b1 byte
|
||||
i int64
|
||||
b2 byte
|
||||
}
|
||||
|
||||
func sum1(foos []Foo1) int64 {
|
||||
var s int64
|
||||
for i := 0; i < len(foos); i++ {
|
||||
s += foos[i].i
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type Foo2 struct {
|
||||
i int64
|
||||
b1 byte
|
||||
b2 byte
|
||||
}
|
||||
|
||||
func sum2(foos []Foo2) int64 {
|
||||
var s int64
|
||||
for i := 0; i < len(foos); i++ {
|
||||
s += foos[i].i
|
||||
}
|
||||
return s
|
||||
}
|
||||
27
12-optimizations/94-data-alignment/main_test.go
Normal file
27
12-optimizations/94-data-alignment/main_test.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
var global int64
|
||||
|
||||
func BenchmarkSum1(b *testing.B) {
|
||||
var local int64
|
||||
s := make([]Foo1, n)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = sum1(s)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkSum2(b *testing.B) {
|
||||
var local int64
|
||||
s := make([]Foo2, n)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = sum2(s)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
41
12-optimizations/95-stack-heap/main.go
Normal file
41
12-optimizations/95-stack-heap/main.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
func listing1() {
|
||||
a := 3
|
||||
b := 2
|
||||
|
||||
c := sumValue(a, b)
|
||||
println(c)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sumValue(x, y int) int {
|
||||
z := x + y
|
||||
return z
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
a := 3
|
||||
b := 2
|
||||
|
||||
c := sumPtr(a, b)
|
||||
println(*c)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sumPtr(x, y int) *int {
|
||||
z := x + y
|
||||
return &z
|
||||
}
|
||||
|
||||
func listing3() {
|
||||
a := 3
|
||||
b := 2
|
||||
c := sum(&a, &b)
|
||||
println(c)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sum(x, y *int) int {
|
||||
return *x + *y
|
||||
}
|
||||
26
12-optimizations/95-stack-heap/main_test.go
Normal file
26
12-optimizations/95-stack-heap/main_test.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
var (
|
||||
globalValue int
|
||||
globalPtr *int
|
||||
)
|
||||
|
||||
func BenchmarkSumValue(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
var local int
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = sumValue(i, i)
|
||||
}
|
||||
globalValue = local
|
||||
}
|
||||
|
||||
func BenchmarkSumPtr(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
var local *int
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = sumPtr(i, i)
|
||||
}
|
||||
globalValue = *local
|
||||
}
|
||||
16
12-optimizations/96-reduce-allocations/compiler/main.go
Normal file
16
12-optimizations/96-reduce-allocations/compiler/main.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package main
|
||||
|
||||
type cache struct {
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
func (c *cache) get1(bytes []byte) (v int, contains bool) {
|
||||
key := string(bytes)
|
||||
v, contains = c.m[key]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cache) get2(bytes []byte) (v int, contains bool) {
|
||||
v, contains = c.m[string(bytes)]
|
||||
return
|
||||
}
|
||||
24
12-optimizations/96-reduce-allocations/sync-pool/main.go
Normal file
24
12-optimizations/96-reduce-allocations/sync-pool/main.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var pool = sync.Pool{
|
||||
New: func() any {
|
||||
return make([]byte, 1024)
|
||||
},
|
||||
}
|
||||
|
||||
func write(w io.Writer) {
|
||||
buffer := pool.Get().([]byte)
|
||||
buffer = buffer[:0]
|
||||
defer pool.Put(buffer)
|
||||
|
||||
getResponse(buffer)
|
||||
_, _ = w.Write(buffer)
|
||||
}
|
||||
|
||||
func getResponse([]byte) {
|
||||
}
|
||||
75
2-code-project-organization/1-variable-shadowing/main.go
Normal file
75
2-code-project-organization/1-variable-shadowing/main.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func listing1() error {
|
||||
var client *http.Client
|
||||
if tracing {
|
||||
client, err := createClientWithTracing()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println(client)
|
||||
} else {
|
||||
client, err := createDefaultClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println(client)
|
||||
}
|
||||
|
||||
_ = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing2() error {
|
||||
var client *http.Client
|
||||
if tracing {
|
||||
c, err := createClientWithTracing()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client = c
|
||||
} else {
|
||||
c, err := createDefaultClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client = c
|
||||
}
|
||||
|
||||
_ = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func listing3() error {
|
||||
var client *http.Client
|
||||
var err error
|
||||
if tracing {
|
||||
client, err = createClientWithTracing()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
client, err = createDefaultClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_ = client
|
||||
return nil
|
||||
}
|
||||
|
||||
var tracing bool
|
||||
|
||||
func createClientWithTracing() (*http.Client, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func createDefaultClient() (*http.Client, error) {
|
||||
return nil, nil
|
||||
}
|
||||
54
2-code-project-organization/10-type-embedding/main.go
Normal file
54
2-code-project-organization/10-type-embedding/main.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Foo struct {
|
||||
Bar
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
Baz int
|
||||
}
|
||||
|
||||
func fooBar() {
|
||||
foo := Foo{}
|
||||
foo.Baz = 42
|
||||
}
|
||||
|
||||
type InMem struct {
|
||||
sync.Mutex
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
func New() *InMem {
|
||||
return &InMem{m: make(map[string]int)}
|
||||
}
|
||||
|
||||
func (i *InMem) Get(key string) (int, bool) {
|
||||
i.Lock()
|
||||
v, contains := i.m[key]
|
||||
i.Unlock()
|
||||
return v, contains
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
writeCloser io.WriteCloser
|
||||
}
|
||||
|
||||
func (l Logger) Write(p []byte) (int, error) {
|
||||
return l.writeCloser.Write(p)
|
||||
}
|
||||
|
||||
func (l Logger) Close() error {
|
||||
return l.writeCloser.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
l := Logger{writeCloser: os.Stdout}
|
||||
_, _ = l.Write([]byte("foo"))
|
||||
_ = l.Close()
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const defaultHTTPPort = 8080
|
||||
|
||||
type Config struct {
|
||||
Port int
|
||||
}
|
||||
|
||||
type ConfigBuilder struct {
|
||||
port *int
|
||||
}
|
||||
|
||||
func (b *ConfigBuilder) Port(port int) *ConfigBuilder {
|
||||
b.port = &port
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ConfigBuilder) Build() (Config, error) {
|
||||
cfg := Config{}
|
||||
|
||||
if b.port == nil {
|
||||
cfg.Port = defaultHTTPPort
|
||||
} else {
|
||||
if *b.port == 0 {
|
||||
cfg.Port = randomPort()
|
||||
} else if *b.port < 0 {
|
||||
return Config{}, errors.New("port should be positive")
|
||||
} else {
|
||||
cfg.Port = *b.port
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func NewServer(addr string, config Config) (*http.Server, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func client() error {
|
||||
builder := ConfigBuilder{}
|
||||
builder.Port(8080)
|
||||
cfg, err := builder.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := NewServer("localhost", cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = server
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomPort() int {
|
||||
return 4 // Chosen by fair dice roll, guaranteed to be random.
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
type Config struct {
|
||||
Port int
|
||||
}
|
||||
|
||||
func NewServer(addr string, cfg Config) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
NewServer("localhost", Config{})
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const defaultHTTPPort = 8080
|
||||
|
||||
type options struct {
|
||||
port *int
|
||||
}
|
||||
|
||||
type Option func(options *options) error
|
||||
|
||||
func WithPort(port int) Option {
|
||||
return func(options *options) error {
|
||||
if port < 0 {
|
||||
return errors.New("port should be positive")
|
||||
}
|
||||
options.port = &port
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewServer(addr string, opts ...Option) (*http.Server, error) {
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
err := opt(&options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// At this stage, the options struct is built and contains the config
|
||||
// Therefore, we can implement our logic related to port configuration
|
||||
var port int
|
||||
if options.port == nil {
|
||||
port = defaultHTTPPort
|
||||
} else {
|
||||
if *options.port == 0 {
|
||||
port = randomPort()
|
||||
} else {
|
||||
port = *options.port
|
||||
}
|
||||
}
|
||||
|
||||
_ = port
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func client() {
|
||||
_, _ = NewServer("localhost", WithPort(8080))
|
||||
}
|
||||
|
||||
func randomPort() int {
|
||||
return 4 // Chosen by fair dice roll, guaranteed to be random.
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package stringset
|
||||
|
||||
type Set map[string]struct{}
|
||||
|
||||
func New(...string) Set { return nil }
|
||||
|
||||
func (s Set) Sort() []string { return nil }
|
||||
45
2-code-project-organization/2-nested-code/main.go
Normal file
45
2-code-project-organization/2-nested-code/main.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
func join1(s1, s2 string, max int) (string, error) {
|
||||
if s1 == "" {
|
||||
return "", errors.New("s1 is empty")
|
||||
} else {
|
||||
if s2 == "" {
|
||||
return "", errors.New("s2 is empty")
|
||||
} else {
|
||||
concat, err := concatenate(s1, s2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
if len(concat) > max {
|
||||
return concat[:max], nil
|
||||
} else {
|
||||
return concat, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func join2(s1, s2 string, max int) (string, error) {
|
||||
if s1 == "" {
|
||||
return "", errors.New("s1 is empty")
|
||||
}
|
||||
if s2 == "" {
|
||||
return "", errors.New("s2 is empty")
|
||||
}
|
||||
concat, err := concatenate(s1, s2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(concat) > max {
|
||||
return concat[:max], nil
|
||||
}
|
||||
return concat, nil
|
||||
}
|
||||
|
||||
func concatenate(s1, s2 string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
22
2-code-project-organization/3-init-functions/db/main.go
Normal file
22
2-code-project-organization/3-init-functions/db/main.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func init() {
|
||||
dataSourceName := os.Getenv("MYSQL_DATA_SOURCE_NAME")
|
||||
d, err := sql.Open("mysql", dataSourceName)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
err = d.Ping()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
db = d
|
||||
}
|
||||
20
2-code-project-organization/3-init-functions/main/main.go
Normal file
20
2-code-project-organization/3-init-functions/main/main.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/teivah/100-go-mistakes/2-code-project-organization/3-init-functions/redis"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("init 1")
|
||||
}
|
||||
|
||||
func init() {
|
||||
fmt.Println("init 2")
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := redis.Store("foo", "bar")
|
||||
_ = err
|
||||
}
|
||||
11
2-code-project-organization/3-init-functions/redis/redis.go
Normal file
11
2-code-project-organization/3-init-functions/redis/redis.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package redis
|
||||
|
||||
import "fmt"
|
||||
|
||||
func init() {
|
||||
fmt.Println("redis")
|
||||
}
|
||||
|
||||
func Store(key, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import "io"
|
||||
|
||||
func copySourceToDest(source io.Reader, dest io.Writer) error {
|
||||
b, err := io.ReadAll(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dest.Write(b)
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopySourceToDest(t *testing.T) {
|
||||
const input = "foo"
|
||||
source := strings.NewReader(input)
|
||||
dest := bytes.NewBuffer(make([]byte, 0))
|
||||
|
||||
err := copySourceToDest(source, dest)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
got := dest.String()
|
||||
if got != input {
|
||||
t.Errorf("expected: %s, got: %s", input, got)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package main
|
||||
|
||||
type customerStorer interface {
|
||||
StoreCustomer(Customer) error
|
||||
}
|
||||
|
||||
type CustomerService2 struct {
|
||||
storer customerStorer
|
||||
}
|
||||
|
||||
func (cs CustomerService2) CreateNewCustomer(id string) error {
|
||||
customer := Customer{id: id}
|
||||
return cs.storer.StoreCustomer(customer)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
type CustomerService struct {
|
||||
store Store
|
||||
}
|
||||
|
||||
func (cs CustomerService) CreateNewCustomer(id string) error {
|
||||
customer := Customer{id: id}
|
||||
return cs.store.StoreCustomer(customer)
|
||||
}
|
||||
|
||||
type Customer struct {
|
||||
id string
|
||||
}
|
||||
|
||||
type Store struct{}
|
||||
|
||||
func (s Store) StoreCustomer(customer Customer) error {
|
||||
return nil
|
||||
}
|
||||
18
2-code-project-organization/8-empty-interface/main.go
Normal file
18
2-code-project-organization/8-empty-interface/main.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
var i any
|
||||
|
||||
i = 42
|
||||
i = "foo"
|
||||
i = struct {
|
||||
s string
|
||||
}{
|
||||
s: "bar",
|
||||
}
|
||||
i = f
|
||||
|
||||
_ = i
|
||||
}
|
||||
|
||||
func f() {}
|
||||
17
2-code-project-organization/8-empty-interface/store/after.go
Normal file
17
2-code-project-organization/8-empty-interface/store/after.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package store
|
||||
|
||||
func (s *Store) GetContract(id string) (Contract, error) {
|
||||
return Contract{}, nil
|
||||
}
|
||||
|
||||
func (s *Store) SetContract(id string, contract Contract) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) GetCustomer(id string) (Customer, error) {
|
||||
return Customer{}, nil
|
||||
}
|
||||
|
||||
func (s *Store) SetCustomer(id string, customer Customer) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package store
|
||||
|
||||
type Customer struct {
|
||||
// Some fields
|
||||
}
|
||||
|
||||
type Contract struct {
|
||||
// Some fields
|
||||
}
|
||||
|
||||
type Store struct{}
|
||||
|
||||
func (s *Store) Get(id string) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Store) Set(id string, v any) error {
|
||||
return nil
|
||||
}
|
||||
54
2-code-project-organization/9-generics/main.go
Normal file
54
2-code-project-organization/9-generics/main.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getKeys(m any) ([]any, error) {
|
||||
switch t := m.(type) {
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown type: %T", t)
|
||||
case map[string]int:
|
||||
var keys []any
|
||||
for k := range t {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys, nil
|
||||
case map[int]string:
|
||||
// ...
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getKeysGenerics[K comparable, V any](m map[K]V) []K {
|
||||
var keys []K
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
type customConstraint interface {
|
||||
~int | ~string <1>
|
||||
}
|
||||
|
||||
func getKeysWithConstraing[K customConstraint, V any](m map[K]V) []K {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Node[T any] struct {
|
||||
Val T
|
||||
next *Node[T]
|
||||
}
|
||||
|
||||
func (n *Node[T]) Add(next *Node[T]) {
|
||||
n.next = next
|
||||
}
|
||||
|
||||
type sliceFn[T any] struct {
|
||||
s []T
|
||||
compare func(T, T) bool
|
||||
}
|
||||
|
||||
func (s sliceFn[T]) Len() int { return len(s.s) }
|
||||
func (s sliceFn[T]) Less(i, j int) bool { return s.compare(s.s[i], s.s[j]) }
|
||||
func (s sliceFn[T]) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
|
||||
8
3-data-types/17-octal-literals/main.go
Normal file
8
3-data-types/17-octal-literals/main.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
sum := 100 + 0o10
|
||||
fmt.Println(sum)
|
||||
}
|
||||
50
3-data-types/18-integer-overflows/main.go
Normal file
50
3-data-types/18-integer-overflows/main.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package main
|
||||
|
||||
import "math"
|
||||
|
||||
func Inc32(counter int32) int32 {
|
||||
if counter == math.MaxInt32 {
|
||||
panic("int32 overflow")
|
||||
}
|
||||
return counter + 1
|
||||
}
|
||||
|
||||
func IncInt(counter int) int {
|
||||
if counter == math.MaxInt {
|
||||
panic("int overflow")
|
||||
}
|
||||
return counter + 1
|
||||
}
|
||||
|
||||
func IncUint(counter uint) uint {
|
||||
if counter == math.MaxUint {
|
||||
panic("uint overflow")
|
||||
}
|
||||
return counter + 1
|
||||
}
|
||||
|
||||
func AddInt(a, b int) int {
|
||||
if a > math.MaxInt-b {
|
||||
panic("int overflow")
|
||||
}
|
||||
|
||||
return a + b
|
||||
}
|
||||
|
||||
func MultiplyInt(a, b int) int {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
result := a * b
|
||||
if a == 1 || b == 1 {
|
||||
return result
|
||||
}
|
||||
if a == math.MinInt || b == math.MinInt {
|
||||
panic("integer overflow")
|
||||
}
|
||||
if result/b != a {
|
||||
panic("integer overflow")
|
||||
}
|
||||
return result
|
||||
}
|
||||
24
3-data-types/19-floating-points/main.go
Normal file
24
3-data-types/19-floating-points/main.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var n float32 = 1.0001
|
||||
fmt.Println(n * n)
|
||||
}
|
||||
|
||||
func f1(n int) float64 {
|
||||
result := 10_000.
|
||||
for i := 0; i < n; i++ {
|
||||
result += 1.0001
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func f2(n int) float64 {
|
||||
result := 0.
|
||||
for i := 0; i < n; i++ {
|
||||
result += 1.0001
|
||||
}
|
||||
return result + 10_000.
|
||||
}
|
||||
38
3-data-types/20-slice-length-cap/main.go
Normal file
38
3-data-types/20-slice-length-cap/main.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]int, 3, 6)
|
||||
print(s)
|
||||
|
||||
s[1] = 1
|
||||
print(s)
|
||||
|
||||
s = append(s, 2)
|
||||
print(s)
|
||||
|
||||
s = append(s, 3)
|
||||
s = append(s, 4)
|
||||
s = append(s, 5)
|
||||
print(s)
|
||||
|
||||
s1 := make([]int, 3, 6)
|
||||
s2 := s1[1:3]
|
||||
s1[1] = 1
|
||||
print(s2)
|
||||
|
||||
s2 = append(s2, 2)
|
||||
print(s1)
|
||||
print(s2)
|
||||
|
||||
s2 = append(s2, 3)
|
||||
s2 = append(s2, 4)
|
||||
s2 = append(s2, 5)
|
||||
print(s1)
|
||||
print(s2)
|
||||
}
|
||||
|
||||
func print(s []int) {
|
||||
fmt.Printf("len=%d, cap=%d: %v\n", len(s), cap(s), s)
|
||||
}
|
||||
38
3-data-types/21-slice-init/main.go
Normal file
38
3-data-types/21-slice-init/main.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
func convertEmptySlice(foos []Foo) []Bar {
|
||||
bars := make([]Bar, 0)
|
||||
|
||||
for _, foo := range foos {
|
||||
bars = append(bars, fooToBar(foo))
|
||||
}
|
||||
return bars
|
||||
}
|
||||
|
||||
func convertGivenCapacity(foos []Foo) []Bar {
|
||||
n := len(foos)
|
||||
bars := make([]Bar, 0, n)
|
||||
|
||||
for _, foo := range foos {
|
||||
bars = append(bars, fooToBar(foo))
|
||||
}
|
||||
return bars
|
||||
}
|
||||
|
||||
func convertGivenLength(foos []Foo) []Bar {
|
||||
n := len(foos)
|
||||
bars := make([]Bar, n)
|
||||
|
||||
for i, foo := range foos {
|
||||
bars[i] = fooToBar(foo)
|
||||
}
|
||||
return bars
|
||||
}
|
||||
|
||||
type Foo struct{}
|
||||
|
||||
type Bar struct{}
|
||||
|
||||
func fooToBar(foo Foo) Bar {
|
||||
return Bar{}
|
||||
}
|
||||
37
3-data-types/21-slice-init/main_test.go
Normal file
37
3-data-types/21-slice-init/main_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
var global []Bar
|
||||
|
||||
func BenchmarkConvert_EmptySlice(b *testing.B) {
|
||||
var local []Bar
|
||||
foos := make([]Foo, n)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = convertEmptySlice(foos)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkConvert_GivenCapacity(b *testing.B) {
|
||||
var local []Bar
|
||||
foos := make([]Foo, n)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = convertGivenCapacity(foos)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkConvert_GivenLength(b *testing.B) {
|
||||
var local []Bar
|
||||
foos := make([]Foo, n)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
local = convertGivenLength(foos)
|
||||
}
|
||||
global = local
|
||||
}
|
||||
29
3-data-types/22-nil-empty-slice/json/main.go
Normal file
29
3-data-types/22-nil-empty-slice/json/main.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var s1 []float32
|
||||
customer1 := customer{
|
||||
ID: "foo",
|
||||
Operations: s1,
|
||||
}
|
||||
b, _ := json.Marshal(customer1)
|
||||
fmt.Println(string(b))
|
||||
|
||||
s2 := make([]float32, 0)
|
||||
customer2 := customer{
|
||||
ID: "bar",
|
||||
Operations: s2,
|
||||
}
|
||||
b, _ = json.Marshal(customer2)
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
type customer struct {
|
||||
ID string
|
||||
Operations []float32
|
||||
}
|
||||
23
3-data-types/22-nil-empty-slice/slice-init/main.go
Normal file
23
3-data-types/22-nil-empty-slice/slice-init/main.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var s []string
|
||||
log(1, s)
|
||||
|
||||
s = []string(nil)
|
||||
log(2, s)
|
||||
|
||||
s = []string{}
|
||||
log(3, s)
|
||||
|
||||
s = make([]string, 0)
|
||||
log(4, s)
|
||||
}
|
||||
|
||||
func log(i int, s []string) {
|
||||
fmt.Printf("%d: empty=%t\tnil=%t\n", i, len(s) == 0, s == nil)
|
||||
}
|
||||
29
3-data-types/23-checking-slice-empty/main.go
Normal file
29
3-data-types/23-checking-slice-empty/main.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
func handleOperations1(id string) {
|
||||
operations := getOperations(id)
|
||||
if operations != nil {
|
||||
handle(operations)
|
||||
}
|
||||
}
|
||||
|
||||
func handleOperations2(id string) {
|
||||
operations := getOperations(id)
|
||||
if len(operations) != 0 {
|
||||
handle(operations)
|
||||
}
|
||||
}
|
||||
|
||||
func getOperations(id string) []float32 {
|
||||
operations := make([]float32, 0)
|
||||
|
||||
if id == "" {
|
||||
return operations
|
||||
}
|
||||
|
||||
// Add elements to operations
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
func handle(operations []float32) {}
|
||||
23
3-data-types/24-slice-copy/main.go
Normal file
23
3-data-types/24-slice-copy/main.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func bad() {
|
||||
src := []int{0, 1, 2}
|
||||
var dst []int
|
||||
copy(dst, src)
|
||||
fmt.Println(dst)
|
||||
|
||||
_ = src
|
||||
_ = dst
|
||||
}
|
||||
|
||||
func correct() {
|
||||
src := []int{0, 1, 2}
|
||||
dst := make([]int, len(src))
|
||||
copy(dst, src)
|
||||
fmt.Println(dst)
|
||||
|
||||
_ = src
|
||||
_ = dst
|
||||
}
|
||||
36
3-data-types/25-slice-append/25-slice-append.go
Normal file
36
3-data-types/25-slice-append/25-slice-append.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
listing1()
|
||||
listing2()
|
||||
listing3()
|
||||
}
|
||||
|
||||
func listing1() {
|
||||
s := []int{1, 2, 3}
|
||||
|
||||
f(s[:2])
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
s := []int{1, 2, 3}
|
||||
sCopy := make([]int, 2)
|
||||
copy(sCopy, s)
|
||||
|
||||
f(sCopy)
|
||||
result := append(sCopy, s[2])
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func listing3() {
|
||||
s := []int{1, 2, 3}
|
||||
f(s[:2:2])
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
func f(s []int) {
|
||||
_ = append(s, 10)
|
||||
}
|
||||
36
3-data-types/26-slice-memory-leak/capacity-leak/main.go
Normal file
36
3-data-types/26-slice-memory-leak/capacity-leak/main.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func consumeMessages() {
|
||||
for {
|
||||
msg := receiveMessage()
|
||||
// Do something with msg
|
||||
storeMessageType(getMessageType(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func getMessageType(msg []byte) []byte {
|
||||
return msg[:5]
|
||||
}
|
||||
|
||||
func getMessageTypeWithCopy(msg []byte) []byte {
|
||||
msgType := make([]byte, 5)
|
||||
copy(msgType, msg)
|
||||
return msgType
|
||||
}
|
||||
|
||||
func receiveMessage() []byte {
|
||||
return make([]byte, 1_000_000)
|
||||
}
|
||||
|
||||
func storeMessageType([]byte) {}
|
||||
|
||||
func printAlloc() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("%d KB\n", m.Alloc/1024)
|
||||
}
|
||||
50
3-data-types/26-slice-memory-leak/slice-pointers/main.go
Normal file
50
3-data-types/26-slice-memory-leak/slice-pointers/main.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Foo struct {
|
||||
v []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
foos := make([]Foo, 1_000)
|
||||
printAlloc()
|
||||
|
||||
for i := 0; i < len(foos); i++ {
|
||||
foos[i] = Foo{
|
||||
v: make([]byte, 1024*1024),
|
||||
}
|
||||
}
|
||||
printAlloc()
|
||||
|
||||
two := keepFirstTwoElementsOnly(foos)
|
||||
runtime.GC()
|
||||
printAlloc()
|
||||
runtime.KeepAlive(two)
|
||||
}
|
||||
|
||||
func keepFirstTwoElementsOnly(foos []Foo) []Foo {
|
||||
return foos[:2]
|
||||
}
|
||||
|
||||
func keepFirstTwoElementsOnlyCopy(foos []Foo) []Foo {
|
||||
res := make([]Foo, 2)
|
||||
copy(res, foos)
|
||||
return res
|
||||
}
|
||||
|
||||
func keepFirstTwoElementsOnlyMarkNil(foos []Foo) []Foo {
|
||||
for i := 2; i < len(foos); i++ {
|
||||
foos[i].v = nil
|
||||
}
|
||||
return foos[:2]
|
||||
}
|
||||
|
||||
func printAlloc() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("%d KB\n", m.Alloc/1024)
|
||||
}
|
||||
29
3-data-types/27-map-init/main_test.go
Normal file
29
3-data-types/27-map-init/main_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
const n = 1_000_000
|
||||
|
||||
var global map[int]struct{}
|
||||
|
||||
func BenchmarkMapWithoutSize(b *testing.B) {
|
||||
var local map[int]struct{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
m := make(map[int]struct{})
|
||||
for j := 0; j < n; j++ {
|
||||
m[j] = struct{}{}
|
||||
}
|
||||
}
|
||||
global = local
|
||||
}
|
||||
|
||||
func BenchmarkMapWithSize(b *testing.B) {
|
||||
var local map[int]struct{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
m := make(map[int]struct{}, n)
|
||||
for j := 0; j < n; j++ {
|
||||
m[j] = struct{}{}
|
||||
}
|
||||
}
|
||||
global = local
|
||||
}
|
||||
39
3-data-types/28-map-memory-leak/main.go
Normal file
39
3-data-types/28-map-memory-leak/main.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Init
|
||||
n := 1_000_000
|
||||
m := make(map[int][128]byte)
|
||||
printAlloc()
|
||||
|
||||
// Add elements
|
||||
for i := 0; i < n; i++ {
|
||||
m[i] = randBytes()
|
||||
}
|
||||
printAlloc()
|
||||
|
||||
// Remove elements
|
||||
for i := 0; i < n; i++ {
|
||||
delete(m, i)
|
||||
}
|
||||
|
||||
// End
|
||||
runtime.GC()
|
||||
printAlloc()
|
||||
runtime.KeepAlive(m)
|
||||
}
|
||||
|
||||
func randBytes() [128]byte {
|
||||
return [128]byte{}
|
||||
}
|
||||
|
||||
func printAlloc() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("%d MB\n", m.Alloc/1024/1024)
|
||||
}
|
||||
55
3-data-types/29-comparing-values/main.go
Normal file
55
3-data-types/29-comparing-values/main.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type customer1 struct {
|
||||
id string
|
||||
}
|
||||
|
||||
type customer2 struct {
|
||||
id string
|
||||
operations []float64
|
||||
}
|
||||
|
||||
func main() {
|
||||
cust11 := customer1{id: "x"}
|
||||
cust12 := customer1{id: "x"}
|
||||
fmt.Println(cust11 == cust12)
|
||||
|
||||
cust21 := customer2{id: "x", operations: []float64{1.}}
|
||||
cust22 := customer2{id: "x", operations: []float64{1.}}
|
||||
// Doesn't compile
|
||||
// fmt.Println(cust21 == cust22)
|
||||
_ = cust21
|
||||
_ = cust22
|
||||
|
||||
var a any = 3
|
||||
var b any = 3
|
||||
fmt.Println(a == b)
|
||||
|
||||
var cust31 any = customer2{id: "x", operations: []float64{1.}}
|
||||
var cust32 any = customer2{id: "x", operations: []float64{1.}}
|
||||
fmt.Println(cust31 == cust32)
|
||||
|
||||
cust41 := customer2{id: "x", operations: []float64{1.}}
|
||||
cust42 := customer2{id: "x", operations: []float64{1.}}
|
||||
fmt.Println(reflect.DeepEqual(cust41, cust42))
|
||||
}
|
||||
|
||||
func (a customer2) equal(b customer2) bool {
|
||||
if a.id != b.id {
|
||||
return false
|
||||
}
|
||||
if len(a.operations) != len(b.operations) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a.operations); i++ {
|
||||
if a.operations[i] != b.operations[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := []string{"a", "b", "c"}
|
||||
for i, v := range s {
|
||||
fmt.Printf("index=%d, value=%s\n", i, v)
|
||||
}
|
||||
|
||||
for _, v := range s {
|
||||
fmt.Printf("value=%s\n", v)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type account struct {
|
||||
balance float32
|
||||
}
|
||||
|
||||
func main() {
|
||||
accounts := createAccounts()
|
||||
for _, a := range accounts {
|
||||
a.balance += 1000
|
||||
}
|
||||
fmt.Println(accounts)
|
||||
|
||||
accounts = createAccounts()
|
||||
for i := range accounts {
|
||||
accounts[i].balance += 1000
|
||||
}
|
||||
fmt.Println(accounts)
|
||||
|
||||
accounts = createAccounts()
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
accounts[i].balance += 1000
|
||||
}
|
||||
fmt.Println(accounts)
|
||||
|
||||
accountsPtr := createAccountsPtr()
|
||||
for _, a := range accountsPtr {
|
||||
a.balance += 1000
|
||||
}
|
||||
printAccountsPtr(accountsPtr)
|
||||
}
|
||||
|
||||
func createAccounts() []account {
|
||||
return []account{
|
||||
{balance: 100.},
|
||||
{balance: 200.},
|
||||
{balance: 300.},
|
||||
}
|
||||
}
|
||||
|
||||
func createAccountsPtr() []*account {
|
||||
return []*account{
|
||||
{balance: 100.},
|
||||
{balance: 200.},
|
||||
{balance: 300.},
|
||||
}
|
||||
}
|
||||
|
||||
func printAccountsPtr(accounts []*account) {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("[")
|
||||
s := make([]string, len(accounts))
|
||||
for i, account := range accounts {
|
||||
s[i] = fmt.Sprintf("{%.0f}", account.balance)
|
||||
}
|
||||
sb.WriteString(strings.Join(s, " "))
|
||||
sb.WriteString("]")
|
||||
fmt.Println(sb.String())
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func listing1() {
|
||||
a := [3]int{0, 1, 2}
|
||||
for i, v := range a {
|
||||
a[2] = 10
|
||||
if i == 2 {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
a := [3]int{0, 1, 2}
|
||||
for i := range a {
|
||||
a[2] = 10
|
||||
if i == 2 {
|
||||
fmt.Println(a[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing3() {
|
||||
a := [3]int{0, 1, 2}
|
||||
for i, v := range &a {
|
||||
a[2] = 10
|
||||
if i == 2 {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
ch1 := make(chan int, 3)
|
||||
go func() {
|
||||
ch1 <- 0
|
||||
ch1 <- 1
|
||||
ch1 <- 2
|
||||
close(ch1)
|
||||
}()
|
||||
|
||||
ch2 := make(chan int, 3)
|
||||
go func() {
|
||||
ch2 <- 10
|
||||
ch2 <- 11
|
||||
ch2 <- 12
|
||||
close(ch2)
|
||||
}()
|
||||
|
||||
ch := ch1
|
||||
for v := range ch {
|
||||
fmt.Println(v)
|
||||
ch = ch2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
s1 := []int{0, 1, 2}
|
||||
for range s1 {
|
||||
s1 = append(s1, 10)
|
||||
}
|
||||
|
||||
s2 := []int{0, 1, 2}
|
||||
for i := 0; i < len(s2); i++ {
|
||||
s2 = append(s2, 10)
|
||||
}
|
||||
}
|
||||
27
4-control-structures/32-range-loop-pointers/concepts/main.go
Normal file
27
4-control-structures/32-range-loop-pointers/concepts/main.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package concepts
|
||||
|
||||
type Store struct {
|
||||
m map[string]*Foo
|
||||
}
|
||||
|
||||
func (s Store) Put(id string, foo *Foo) {
|
||||
s.m[id] = foo
|
||||
// ...
|
||||
}
|
||||
|
||||
type Foo struct{}
|
||||
|
||||
func updateMapValue(mapValue map[string]LargeStruct, id string) {
|
||||
value := mapValue[id]
|
||||
value.foo = "bar"
|
||||
mapValue[id] = value
|
||||
}
|
||||
|
||||
func updateMapPointer(mapPointer map[string]*LargeStruct, id string) {
|
||||
mapPointer[id].foo = "bar"
|
||||
}
|
||||
|
||||
type LargeStruct struct {
|
||||
foo string
|
||||
_ [1024]int64
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Customer struct {
|
||||
ID string
|
||||
Balance float64
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
m map[string]*Customer
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := Store{
|
||||
m: make(map[string]*Customer),
|
||||
}
|
||||
s.storeCustomers([]Customer{
|
||||
{ID: "1", Balance: 10},
|
||||
{ID: "2", Balance: -10},
|
||||
{ID: "3", Balance: 0},
|
||||
})
|
||||
print(s.m)
|
||||
}
|
||||
|
||||
func (s *Store) storeCustomers(customers []Customer) {
|
||||
for _, customer := range customers {
|
||||
fmt.Printf("%p\n", &customer)
|
||||
s.m[customer.ID] = &customer
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) storeCustomers2(customers []Customer) {
|
||||
for _, customer := range customers {
|
||||
current := customer
|
||||
s.m[current.ID] = ¤t
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) storeCustomers3(customers []Customer) {
|
||||
for i := range customers {
|
||||
customer := &customers[i]
|
||||
s.m[customer.ID] = customer
|
||||
}
|
||||
}
|
||||
|
||||
func print(m map[string]*Customer) {
|
||||
for k, v := range m {
|
||||
fmt.Printf("key=%s, value=%#v\n", k, v)
|
||||
}
|
||||
}
|
||||
45
4-control-structures/33-map-iteration/main.go
Normal file
45
4-control-structures/33-map-iteration/main.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func listing1() {
|
||||
m := map[int]bool{
|
||||
0: true,
|
||||
1: false,
|
||||
2: true,
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
if v {
|
||||
m[10+k] = true
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
m := map[int]bool{
|
||||
0: true,
|
||||
1: false,
|
||||
2: true,
|
||||
}
|
||||
m2 := copyMap(m)
|
||||
|
||||
for k, v := range m {
|
||||
m2[k] = v
|
||||
if v {
|
||||
m2[10+k] = true
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(m2)
|
||||
}
|
||||
|
||||
func copyMap(m map[int]bool) map[int]bool {
|
||||
res := make(map[int]bool, len(m))
|
||||
for k, v := range m {
|
||||
res[k] = v
|
||||
}
|
||||
return res
|
||||
}
|
||||
54
4-control-structures/34-break/main.go
Normal file
54
4-control-structures/34-break/main.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func listing1() {
|
||||
for i := 0; i < 5; i++ {
|
||||
fmt.Printf("%d ", i)
|
||||
|
||||
switch i {
|
||||
default:
|
||||
case 2:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing2() {
|
||||
loop:
|
||||
for i := 0; i < 5; i++ {
|
||||
fmt.Printf("%d ", i)
|
||||
|
||||
switch i {
|
||||
default:
|
||||
case 2:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing3(ctx context.Context, ch <-chan int) {
|
||||
for {
|
||||
select {
|
||||
case <-ch:
|
||||
// Do something
|
||||
case <-ctx.Done():
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listing4(ctx context.Context, ch <-chan int) {
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-ch:
|
||||
// Do something
|
||||
case <-ctx.Done():
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue