7 Common Mistakes in Go (2015)

80
f e h W U w k M w P n I v Z A S U G k C w P U 7 and when to avoid them Common Mistakes In Go

Transcript of 7 Common Mistakes in Go (2015)

Page 2: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

@Spf13Docker Chief Operator

&

Author of Hugo, Cobra, Afero, Viper

Page 4: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

“Do you want to know the difference between a

master and a beginner?

The master has failed more times than the beginner has tried.”

Page 5: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

–Ed Catmull

“We need to think about failure differently.

Most people think mistakes are a necessary evil. Mistakes aren't a necessary evil, they aren't evil at

all. They are an inevitable consequence of doing something new and as such should be seen

as valuable. “

Page 6: 7 Common Mistakes in Go (2015)
Page 7: 7 Common Mistakes in Go (2015)
Page 9: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

State & Behavior•Types can express state &

behavior •State = data structure •Behavior = methods

Page 10: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Interfaces•One of Go’s most powerful

features •Permits extensibility •Defined by methods •Adherence is only satisfied by

behavior

Page 11: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

•Fastest static site generator

•Native Go •35+ themes •Flexible •100s of contributors •Powers GopherAcademy

gohugo.io

Page 12: 7 Common Mistakes in Go (2015)

func (page *Page) saveSourceAs(path string) {

b := new(bytes.Buffer)

b.Write(page.Source.Content)

page.saveSource(b.Bytes(), path)

}

func (page *Page) saveSource(by []byte, inpath string) {

WriteToDisk(inpath, bytes.NewReader(by))

}

Stop Doing This!!

Page 13: 7 Common Mistakes in Go (2015)

func (page *Page) saveSourceAs(path string) {

b := new(bytes.Buffer)

b.Write(page.Source.Content)

page.saveSource(b.Bytes(), path)

}

func (page *Page) saveSource(by []byte, inpath string) {

WriteToDisk(inpath, bytes.NewReader(by))

}

Stop Doing This!!

Page 14: 7 Common Mistakes in Go (2015)

func (page *Page) saveSourceAs(path string) {

b := new(bytes.Buffer)

b.Write(page.Source.Content)

page.saveSource(b.Bytes(), path)

}

func (page *Page) saveSource(by []byte, inpath string) {

WriteToDisk(inpath, bytes.NewReader(by))

}

Stop Doing This!!

https://github.com/spf13/hugo/blob/master/hugolib/page.go#L582

Page 15: 7 Common Mistakes in Go (2015)

func (page *Page) saveSourceAs(path string) {

b := new(bytes.Buffer)

b.Write(page.Source.Content)

page.saveSource(b, path)

}

func (page *Page) saveSource(b io.Reader, inpath string) {

WriteToDisk(inpath, b)

}

Instead

Page 17: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Io.Reader & Io.Writer•Simple & flexible interfaces

for many operations around input and output

•Provides access to a huge wealth of functionality

•Keeps operations extensible

Page 18: 7 Common Mistakes in Go (2015)

type Reader interface {

Read(p []byte) (n int, err error)

}

type Writer interface {

Write(p []byte) (n int, err error)

}

Io.Reader & Io.Writer

Page 19: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Cobra Cli Commander•Fast and flexible •Powers

Kubernetes & Hugo •Provides subcommands, help,

man pages, bash autocompletegithub.com/spf13/cobra

Page 20: 7 Common Mistakes in Go (2015)

// SetOutput sets the destination for usage and error messages.

// If output is nil, os.Stderr is used.

func (c *Command) SetOutput(o io.Writer) {

c.output = o

}

Cobra Apps Enabled

Page 21: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Viper•Configuration management •Supports json, yaml, toml,

defaults, flags, env vars & remote key value

•Supports nesting, cascading & aliases

github.com/spf13/viper

Page 22: 7 Common Mistakes in Go (2015)

func (v *Viper) ReadBufConfig(buf *bytes.Buffer) error {

v.config = make(map[string]interface{})

v.marshalReader(buf, v.config)

return nil

}

Really Stop Doing This!!

Page 23: 7 Common Mistakes in Go (2015)

func (v *Viper) ReadConfig(in io.Reader) error {

v.config = make(map[string]interface{})

v.marshalReader(in, v.config)

return nil

}

Instead

Page 25: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Interfaces Are Composable

•Functions should only accept interfaces that require the methods they need

•Functions should not accept a broad interface when a narrow one would work

•Compose broad interfaces made from narrower ones

Page 26: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Afero Fs•File system abstraction •Uses standard OS interfaces •Drop in replacement for OS •Great for testing & mocking •Cross platform memory

backed filesystemgithub.com/spf13/afero

Page 31: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Too Many Methods•A lot of people from OO

backgrounds overuse methods

•Natural draw to define everything via structs and methods

Page 32: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

What Is A Function?•Operations performed on N1

inputs that results in N2 outputs •The same inputs will always

result in the same outputs •Functions should not depend on

state

Page 33: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

What Is A Method?•Defines the behavior of a type •A function that operates

against a value •Should use state •Logically connected

Page 34: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Functions Can Be Used With Interfaces

•Methods, by definition, are bound to a specific type

•Functions can accept interfaces as input

Page 35: 7 Common Mistakes in Go (2015)
Page 36: 7 Common Mistakes in Go (2015)

func extractShortcodes(s string, p *Page, t Template) (string, map[string]shortcode, error) { ... for { switch currItem.typ { ... case tError: err := fmt.Errorf("%s:%d: %s", p.BaseFileName(), (p.lineNumRawContentStart() + pt.lexer.lineNum() - 1), currItem) } } ... }

Example From Hugo

Page 37: 7 Common Mistakes in Go (2015)

func extractShortcodes(s string, p *Page, t Template) (string, map[string]shortcode, error) { ... for { switch currItem.typ { ... case tError: err := fmt.Errorf("%s:%d: %s", p.BaseFileName(), (p.lineNumRawContentStart() + pt.lexer.lineNum() - 1), currItem) } } ... }

Example From Hugo

Page 39: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Pointers Vs Values•It’s not a question of performance

(generally), but one of shared access •If you want to share the value with

a function or method, then use a pointer

•If you don’t want to share it, then use a value (copy)

Page 41: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Pointer Receivers•If you want to share a value with

it’s method, use a pointer receiver

•Since methods commonly manage state, this is the common usage

•Not safe for concurrent access

Page 42: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Value Receivers•If you want the value copied

(not shared), use values •If the type is an empty struct

(stateless, just behavior)… then just use value

•Safe for concurrent access

Page 43: 7 Common Mistakes in Go (2015)
Page 44: 7 Common Mistakes in Go (2015)

type InMemoryFile struct { at int64 name string data []byte closed bool }

func (f *InMemoryFile) Close() error { atomic.StoreInt64(&f.at, 0) f.closed = true return nil }

Afero File

Page 48: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Standard Errors•errors.New(“error here”) is

usually sufficient •Exported Error Variables

can be easily checked

Page 49: 7 Common Mistakes in Go (2015)
Page 50: 7 Common Mistakes in Go (2015)

func NewPage(name string) (p *Page, err error) {

if len(name) == 0 {

return nil, errors.New("Zero length page name")

}

Standard Error

Page 51: 7 Common Mistakes in Go (2015)

var ErrNoName = errors.New("Zero length page name")func NewPage(name string) (*Page, error) {

if len(name) == 0 {

return nil, ErrNoName

}

Exported Error Var

Page 52: 7 Common Mistakes in Go (2015)

var ErrNoName = errors.New("Zero length page name") func Foo(name string) (error) {

err := NewPage("bar")

if err == ErrNoName {

newPage("default")

} else {

log.FatalF(err)

}

}

Exported Error Var

Page 53: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Custom Errors•Can provide context to

guarantee consistent feedback •Provide a type which can be

different from the error value •Can provide dynamic values

(based on internal error state)

Page 55: 7 Common Mistakes in Go (2015)

type Error struct {

Code ErrorCode

Message string

Detail interface{}

}

// Error returns a human readable representation of the error.

func (e Error) Error() string {

return fmt.Sprintf("%s: %s",

strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)), e.Message)

}

Internationalization Of Errors

Page 56: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Go StdLib•Standard Go

Libraries •Comprehensive and

powerful •Great examples of “good” Go

http://golang.org/pkg/

Page 57: 7 Common Mistakes in Go (2015)

// Portable analogs of some common system call errors. var ErrInvalid = errors.New("invalid argument") var ErrPermission = errors.New("permission denied")

// PathError records an error and // the operation and file path that caused it. type PathError struct { Op string Path string Err error }

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

Custom Errors : Os

Page 58: 7 Common Mistakes in Go (2015)

// Portable analogs of some common system call errors. var ErrInvalid = errors.New("invalid argument") var ErrPermission = errors.New("permission denied")

// PathError records an error and // the operation and file path that caused it. type PathError struct { Op string Path string Err error }

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

Custom Errors : Os

Page 59: 7 Common Mistakes in Go (2015)

func (f *File) WriteAt(b []byte, off int64) (n int, err error) { if f == nil { return 0, ErrInvalid } for len(b) > 0 { m, e := f.pwrite(b, off) if e != nil { err = &PathError{"write", f.name, e} break } n += m b = b[m:] off += int64(m) } return }

Custom Errors : Os

Page 60: 7 Common Mistakes in Go (2015)

func (f *File) WriteAt(b []byte, off int64) (n int, err error) { if f == nil { return 0, ErrInvalid } for len(b) > 0 { m, e := f.pwrite(b, off) if e != nil { err = &PathError{"write", f.name, e} break } n += m b = b[m:] off += int64(m) } return }

Custom Errors : Os

Page 61: 7 Common Mistakes in Go (2015)

func (f *File) WriteAt(b []byte, off int64) (n int, err error) { if f == nil { return 0, ErrInvalid } for len(b) > 0 { m, e := f.pwrite(b, off) if e != nil { err = &PathError{"write", f.name, e} break } n += m b = b[m:] off += int64(m) } return }

Custom Errors : Os

Page 62: 7 Common Mistakes in Go (2015)

func baa(f *file) error { … n, err := f.WriteAt(x, 3) if _, ok := err.(*PathError) { … } else { log.Fatalf(err) } }

Custom Errors : Os

Page 63: 7 Common Mistakes in Go (2015)

if serr != nil {

if serr, ok := serr.(*PathError); ok && serr.Err == syscall.ENOTDIR {

return nil

}

return serr

Custom Errors : Os

Page 64: 7 Common Mistakes in Go (2015)

if serr != nil {

if serr, ok := serr.(*PathError); ok &&

serr.Err == syscall.ENOTDIR { return nil

}

return serr

Custom Errors : Os

Page 67: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Consider Concurrency

•If you provide a library someone will use it concurrently

•Data structures are not safe for concurrent access

•Values aren’t safe, you need to create safe behavior around them

Page 68: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Making It Safe•Sync package provides behavior

to make a value safe (Atomic/Mutex)

•Channels coordinate values across go routines by permitting one go routine to access at a time

Page 69: 7 Common Mistakes in Go (2015)
Page 70: 7 Common Mistakes in Go (2015)

func (m *MMFS) Create(name string) (File, error) {

m.getData()[name] = MemFileCreate(name)

m.registerDirs(m.getData()[name])

return m.getData()[name], nil

}

Maps Are Not Safe

Page 71: 7 Common Mistakes in Go (2015)

func (m *MMFS) Create(name string) (File, error) {

m.getData()[name] = MemFileCreate(name)

m.registerDirs(m.getData()[name])

return m.getData()[name], nil

}

Maps Are Not Safe

Page 72: 7 Common Mistakes in Go (2015)

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xb code=0x1 addr=0x28 pc=0x1691a7]

goroutine 90 [running]:

runtime.panic(0x501ea0, 0x86b104)

/usr/local/Cellar/go/1.3.3/libexec/src/pkg/runtime/panic.c:279 +0xf5

github.com/spf13/afero.(*MemMapFs).registerDirs(0xc208000860, 0x0, 0x0)

/Users/spf13/gopath/src/github.com/spf13/afero/memmap.go:88 +0x27

Maps Are Not Safe

Page 73: 7 Common Mistakes in Go (2015)

func (m *MMFS) Create(name string) (File, error) {

m.lock()

m.getData()[name] = MemFileCreate(name)

m.unlock()

m.registerDirs(m.getData()[name])

return m.getData()[name], nil

}

Safe Maps With Mutex

Page 74: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Keeping It Unsafe•Safety comes at a cost •Imposes behaviors on consumer •Proper API allows consumers to

add safety as needed •Consumers can use channels or

mutexes

Page 75: 7 Common Mistakes in Go (2015)

func (m *MMFS) Create(name string) (File, error) {

m.lock()

m.getData()[name] = MemFileCreate(name)

m.unlock()

m.registerDirs(m.getData()[name])

return m.getData()[name], nil

}

Safe Maps With Mutex

Page 76: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

Maps Are Unsafe By Design

•Often safety is unnecessary •Enables consumers to

implement safety as needed •Enables consumers to

implement safety as desired

Page 78: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

–Ed Catmull

Failure is a manifestation of learning and exploration.

If you aren't experiencing failure than you are making a far worse

mistake.

You are being driven by the desire to avoid it.

Page 79: 7 Common Mistakes in Go (2015)
Page 80: 7 Common Mistakes in Go (2015)

fe

h

W U

w k

M

w

P n

I

v

ZA

S

U

G

k

Cw

PU

@Spf13Docker Chief of Operations

&

Author of Hugo, Cobra, Afero, Viper