defensive-coding-guide/modules/ROOT/pages/programming-languages/Go.adoc

110 lines
3.7 KiB
Text

:experimental:
:toc:
include::partial$entities.adoc[]
= 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::example$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::example$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 xref:tasks/Tasks-Serialization.adoc[Serialization and Deserialization]).
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.