diff --git a/examples.txt b/examples.txt index b035a04..3fffb46 100644 --- a/examples.txt +++ b/examples.txt @@ -14,8 +14,6 @@ Multiple Return Values Variadic Functions Closures Recursion -# Defer -# Panic # Pointers # New # Structs @@ -43,6 +41,9 @@ Mutexes Stateful Goroutines Sorting Sorting by Functions +Panic +Defer +# Recover # Collection Functions # String Functions # String Formatting diff --git a/examples/defer/defer.go b/examples/defer/defer.go index 037bb53..46b8e67 100644 --- a/examples/defer/defer.go +++ b/examples/defer/defer.go @@ -1,18 +1,44 @@ +// _Defer_ is used to ensure that a function call is +// performed later in a program's execution, usually for +// purposes of cleanup. `defer` is often used where e.g. +// `ensure` and `finally` would be used in other languages. + package main import "fmt" +import "os" -func first() { - fmt.Println("1st") -} - -func second() { - fmt.Println("2nd") -} - +// Suppose we wanted to create a file, write to it, +// and then close when we're done. Here's how we could +// do that with `defer`. func main() { - defer second() - first() + + // Immediately after getting a file object with + // `createFile`, we defer the closing of that file + // with `closeFile`. This will be executed at the end + // of the enclosing function (`main`), after + // `writeFile` has finished. + f := createFile("/tmp/defer.txt") + defer closeFile(f) + writeFile(f) } -// todo: review http://blog.golang.org/2010/08/defer-panic-and-recover.html +func createFile(p string) *os.File { + fmt.Println("creating") + f, err := os.Create(p) + if err != nil { + panic(err) + } + return f +} + +func writeFile(f *os.File) { + fmt.Println("writing") + fmt.Fprintln(f, "data") + +} + +func closeFile(f *os.File) { + fmt.Println("closing") + f.Close() +} diff --git a/examples/defer/defer.sh b/examples/defer/defer.sh new file mode 100644 index 0000000..e84518b --- /dev/null +++ b/examples/defer/defer.sh @@ -0,0 +1,6 @@ +# Running the program confirms that the file is closed +# after being written. +$ go run defer.go +creating +writing +closing diff --git a/examples/panic/panic.go b/examples/panic/panic.go index e39ae61..4c0c9b0 100644 --- a/examples/panic/panic.go +++ b/examples/panic/panic.go @@ -1,13 +1,25 @@ -// A `panic` means something went unexpectedly wrong. -// Mostly we use it to fail fast on errors that -// shouldn't occur during normal operation. +// A `panic` typically means something went unexpectedly +// wrong. Mostly we use it to fail fast on errors that +// shouldn't occur during normal operation, or that we +// aren't prepared to handle gracefully. package main +import "os" + func main() { - // We'll use panic throught this book to check for - // unexpected errors. This is the only program in the - // book designed to panic. - panic("O noes") + // We'll use panic throughout this site to check for + // unexpected errors. This is the only program on the + // site designed to panic. + panic("a problem") + + // A common use of panic is to abort if function + // returns an error value that we don't know how to + // (or want to) handle. Here's an example of + // `panic`king if we get an unexpected error when creating a new file. + f, err := os.Create("/tmp/file") + if err != nil { + panic(err) + } } diff --git a/examples/panic/panic.sh b/examples/panic/panic.sh index 83dc2ab..d371bff 100644 --- a/examples/panic/panic.sh +++ b/examples/panic/panic.sh @@ -1,7 +1,16 @@ -$ go run 26-panic.go -panic: O noes +# Running the program will cause it to panic, print +# an error message and goroutine traces, and exit with +# a non-zero status. +$ go run panic.go +panic: a problem goroutine 1 [running]: main.main() - /.../src/26-panic.go:4 +0x47 + /.../panic.go:12 +0x47 ... +exit status 2 + +# Note that unlike some languages which use exceptions +# for handling of many errors, in Go it is idiomatic +# to use error-indicating return values wherever possible. +# We'll learn more about this in later examples.