More on this book
Community
Kindle Notes & Highlights
Simplicity requires more work at the beginning of a project to reduce an idea to its essence and more discipline over the lifetime of a project to distinguish good changes from bad or pernicious ones. With sufficient effort, a good change can be accommodated without compromising what Fred Brooks called the “conceptual integrity” of the design but a bad change cannot, and a pernicious change trades simplicity for its shallow cousin, convenience. Only through simplicity of design can a system remain stable, secure, and coherent as it grows.
Package main is special. It defines a standalone executable program, not a library.
A variable can be initialized as part of its declaration. If it is not explicitly initialized, it is implicitly initialized to the zero value for its type, which is 0 for numeric types and the empty string "" for strings.
The for loop is the only loop statement in Go.
each iteration of the loop, range produces a pair of values: the index and the value of the element at that index.
The order of map iteration is not specified, but in practice it is random, varying from one run to another.
The expressions []color.Color{...} and gif.GIF{...} are composite literals
The result is appended, using the built-in append function
If the name begins with an upper-case letter, it is exported, which means that it is visible and accessible outside of its own package
A short variable declaration must declare at least one new variable, however, so this code will not compile:
It is perfectly safe for a function to return the address of a local variable.
There is one exception to this rule: two variables whose type carries no information and is therefore of size zero, such as struct{} or [0]int, may, depending on the implementation, have the same address.
Package initialization begins by initializing package-level variables in the order in which they are declared, except that dependencies are resolved first:
The operator ^ is bitwise exclusive OR (XOR) when used as a binary operator, but when used as a unary prefix operator it is bitwise negation or complement;
Left shifts fill the vacated bits with zeros, as do right shifts of unsigned numbers, but right shifts of signed numbers fill the vacated bits with copies of the sign bit. For this reason, it is important to use unsigned arithmetic when you’re treating an integer as a bit pattern.
Although Go provides unsigned numbers and arithmetic, we tend to use the signed int form even for quantities that can’t be negative, such as the length of an array, though uint might seem a more obvious choice.
The alternative would be calamitous. If len returned an unsigned number, then i too would be a uint, and the condition i >= 0 would always be true by definition. After the third iteration, in which i == 0, the i-- statement would cause i to become not −1, but the maximum uint value (for example, 264−1), and the evaluation of medals[i] would fail at run time, or panic (§5.9), by attempting to access an element outside the bounds of the slice.
Digits may be omitted before the decimal point (.707) or after it (1.).
Boolean values can be combined with the && (AND) and || (OR) operators, which have short-circuit behavior:
Since && has higher precedence than || (mnemonic: && is boolean multiplication, || is boolean addition), no parentheses are required for conditions of this form:
The built-in len function returns the number of bytes (not runes) in a string, and the index operation s[i] retrieves the i-th byte of string s, where 0 ≤ i < len(s).
string value can be written as a string literal, a sequence of bytes enclosed in double quotes:
Sometimes fmt.Scanf is useful for parsing input that consists of orderly mixtures of strings and numbers all on a single line, but it can be inflexible, especially when handling incomplete or irregular input.
Unlike arrays, slices are not comparable, so we cannot use == to test whether two slices contain the same elements.
With the small modification shown below, we can match the behavior of the built-in append. The ellipsis “...” in the declaration of appendInt makes the function variadic: it accepts any number of final arguments.
Though floating-point numbers are comparable, it’s a bad idea to compare floats for equality and, as we mentioned in Chapter 3, especially bad if NaN is a possible value.
All of these operations are safe even if the element isn’t in the map; a map lookup using a key that isn’t present returns the zero value for its type, so, for instance, the following works even when "bob" is not yet a key in the map because the value of ages["bob"] will be 0.
The order of map iteration is unspecified,
Field order is significant to type identity.
A named struct type S can’t declare a field of the same type S: an aggregate value cannot contain itself.
But S may declare a field of the pointer type *S, which lets us create recursive data structures like linked lists and trees.
If all the fields of a struct are comparable, the struct itself is comparable,
Comparable struct types, like other comparable types, may be used as the key type of a map.
Go lets us declare a field with a type but no name; such fields are called anonymous fields.
We say that a Point is embedded within Circle, and a Circle is embedded within Wheel.
Unfortunately, there’s no corresponding shorthand for the struct literal syntax, so neither of these will compile:
Because “anonymous” fields do have implicit names, you can’t have two anonymous fields of the same type since their names would conflict.
Later, we’ll see that anonymous fields need not be struct types;
Composition is central to object-oriented programming in Go, and we’ll explore it further in Section 6.3.
The type of a function is sometimes called its signature
Go has no concept of default parameter values, nor any way to specify arguments by name, so the names of parameters and results don’t matter to the caller except as documentation.
Arguments are passed by value, so the function receives a copy of each argument; modifications to the copy do not affect the caller. However, if the argument contains some kind of reference, like a pointer, slice, map, function, or channel, then the caller may be affected by any modifications the function makes to variables indirectly referred to by the argument.
In contrast, typical Go implementations use variable-size stacks that start small and grow as needed up to a limit on the order of a gigabyte. This lets us use recursion safely and without worrying about overflow.
The fmt.Errorf function formats an error message using fmt.Sprintf and returns a new error value.
Because error messages are frequently chained together, message strings should not be capitalized and newlines should be avoided.
more convenient way to achieve the same effect is to call log.Fatalf.
After checking an error, failure is usually dealt with before success. If failure causes the function to return, the logic for success is not indented within an else block but follows at the outer level.
they are not comparable, so they may not be compared against each other or used as keys in a map.