110 lines
3.7 KiB
Text
110 lines
3.7 KiB
Text
|
|
:experimental:
|
|
|
|
[[chap-Defensive_Coding-Go]]
|
|
= The Go Programming Language
|
|
|
|
This chapter contains language-specific recommendations for Go.
|
|
|
|
[[chap-Defensive_Coding-Go-Memory_Safety]]
|
|
== Memory Safety
|
|
|
|
Go provides memory safety, but only if the program is not executed
|
|
in parallel (that is, `GOMAXPROCS` is not larger than
|
|
`1`). The reason is that interface values and
|
|
slices consist of multiple words are not updated atomically.
|
|
Another thread of execution can observe an inconsistent pairing
|
|
between type information and stored value (for interfaces) or
|
|
pointer and length (for slices), and such inconsistency can lead
|
|
to a memory safety violation.
|
|
|
|
Code which does not run in parallel and does not use the
|
|
`unsafe` package (or other packages which expose
|
|
unsafe constructs) is memory-safe. For example, invalid casts and
|
|
out-of-range subscripting cause panics at run time.
|
|
|
|
Keep in mind that finalization can introduce parallelism because
|
|
finalizers are executed concurrently, potentially interleaved with
|
|
the rest of the program.
|
|
|
|
[[chap-Defensive_Coding-Go-Error_Handling]]
|
|
== Error Handling
|
|
|
|
Only a few common operations (such as pointer dereference, integer
|
|
division, array subscripting) trigger exceptions in Go, called
|
|
*panics*. Most interfaces in the standard
|
|
library use a separate return value of type
|
|
`error` to signal error.
|
|
|
|
Not checking error return values can lead to incorrect operation
|
|
and data loss (especially in the case of writes, using interfaces
|
|
such as `io.Writer`).
|
|
|
|
The correct way to check error return values depends on the
|
|
function or method being called. In the majority of cases, the
|
|
first step after calling a function should be an error check
|
|
against the `nil` value, handling any encountered
|
|
error. See <<ex-Defensive_Coding-Go-Error_Handling-Regular>> for
|
|
details.
|
|
|
|
[[ex-Defensive_Coding-Go-Error_Handling-Regular]]
|
|
.Regular error handling in Go
|
|
====
|
|
|
|
[source,go]
|
|
----
|
|
include::../snippets/Go-Error_Handling-Regular.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
However, with `io.Reader`,
|
|
`io.ReaderAt` and related interfaces, it is
|
|
necessary to check for a non-zero number of read bytes first, as
|
|
shown in <<ex-Defensive_Coding-Go-Error_Handling-IO>>. If this
|
|
pattern is not followed, data loss may occur. This is due to the
|
|
fact that the `io.Reader` interface permits
|
|
returning both data and an error at the same time.
|
|
|
|
[[ex-Defensive_Coding-Go-Error_Handling-IO]]
|
|
.Read error handling in Go
|
|
====
|
|
|
|
[source,go]
|
|
----
|
|
include::../snippets/Go-Error_Handling-IO.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
[[chap-Defensive_Coding-Go-Garbage_Collector]]
|
|
== Garbage Collector
|
|
|
|
Older Go releases (before Go 1.3) use a conservative garbage
|
|
collector without blacklisting. This means that data blobs can
|
|
cause retention of unrelated data structures because the data is
|
|
conservatively interpreted as pointers. This phenomenon can be
|
|
triggered accidentally on 32-bit architectures and is more likely
|
|
to occur if the heap grows larger. On 64-bit architectures, it
|
|
may be possible to trigger it deliberately—it is unlikely to occur
|
|
spontaneously.
|
|
|
|
[[chap-Defensive_Coding-Go-Marshaling]]
|
|
== Marshaling and Unmarshaling
|
|
|
|
Several packages in the `encoding` hierarchy
|
|
provide support for serialization and deserialization. The usual
|
|
caveats apply (see
|
|
<<chap-Defensive_Coding-Tasks-Serialization>>).
|
|
|
|
As an additional precaution, the `Unmarshal`
|
|
and `Decode` functions should only be used with
|
|
fresh values in the `interface{}` argument. This
|
|
is due to the way defaults for missing values are implemented:
|
|
During deserialization, missing value do not result in an error,
|
|
but the original value is preserved. Using a fresh value (with
|
|
suitable default values if necessary) ensures that data from a
|
|
previous deserialization operation does not leak into the current
|
|
one. This is especially relevant when structs are deserialized.
|