for i in 0..N
in some other languages?
reflect
package?
Package developers can put a non-exported zero-size field in a struct type definition, so that compilers will forbid package users using composite literals with some field items but without field names to create values of the struct type.
An example:// foo.go
package foo
type Config struct {
_ [0]int
Name string
Size int
}
// main.go
package main
import "foo"
func main() {
//_ = foo.Config{[0]int{}, "bar", 123} // error
_ = foo.Config{Name: "bar", Size: 123} // compile ok
}
Please try not to place the zero-size non-exported field as the last field in the struct, for doing so might enlarge the size of the struct type.
package main
type T struct {
dummy [0]func()
AnotherField int
}
var x map[T]int // compile error: invalid map key type T
func main() {
var a, b T
_ = a == b // compile error: invalid operation:
}
Currently (Go 1.23), there are some evaluation orders in a multi-value assignment are unspecified when the expressions involved in the multi-value assignment interact with each other. So try to split a multi-value assignment into multiple single value assignments if there are, or you can't make sure whether or not there are, dependencies between the involved expressions.
In fact, in some bad-written single-value assignments, there are also expression evaluation order ambiguities. For example, the following program might print[7 0 9]
, [0 8 9]
,
or [7 8 9]
, depending on compiler implementations.
package main
import "fmt"
var a = &[]int{1, 2, 3}
var i int
func f() int {
i = 1
a = &[]int{7, 8, 9}
return 0
}
func main() {
// The evaluation order of "a", "i"
// and "f()" is unspecified.
(*a)[i] = f()
fmt.Println(*a)
}
In other words, a function call in a value assignment may the evaluation results of the non-function-call expressions in the same assignment. Please read evaluation orders in Go for details.
for i in 0..N
in some other languages?package main
import "fmt"
func main() {
const N = 5
for i := range [N]struct{}{} {
fmt.Println(i)
}
for i := range [N][0]int{} {
fmt.Println(i)
}
for i := range (*[N]int)(nil) {
fmt.Println(i)
}
}
Please read how to delete slice elements and kind-of memory leaking caused by not resetting pointers in dead slice elements for details.
Values of the bytes.Buffer
type, strings.Builder
type
and the types in the sync
standard package are not recommended to be copied.
(They really should not be copied, though it is no problems to copy them
under some specified circumstances.)
strings.Builder
will detect
invalid strings.Builder
value copies.
Once such a copy is found, panic will occur. For example:
package main
import "strings"
func main() {
var b strings.Builder
b.WriteString("hello ")
var b2 = b
b2.WriteString("world!") // panic here
}
Copying values of the types in the
sync
standard package will be
warned by the go vet
command provided in Go Toolchain.
// demo.go
package demo
import "sync"
func f(m sync.Mutex) { // warning
m.Lock()
defer m.Unlock()
// do something ...
}
$ go vet demo.go
./demo.go:5: f passes lock by value: sync.Mutex
Copying bytes.Buffer
values will never be detected at run time
nor by the go vet
command. Just be careful not to do this.
Please read the memclr
optimization for details.
reflect
package?M(int) string
.)
package main
import "fmt"
type A int
type B int
func (b B) M(x int) string {
return fmt.Sprint(b, ": ", x)
}
func check(v interface{}) bool {
_, has := v.(interface{M(int) string})
return has
}
func main() {
var a A = 123
var b B = 789
fmt.Println(check(a)) // false
fmt.Println(check(b)) // true
}
Please read this wiki article and this wiki article for details.
func NewX(...Option) *X
function,
and the implementation of this function will merge the input options with some
internal default options, then the following implementation is not recommended.
func NewX(opts ...Option) *X {
options := append(opts, defaultOpts...)
// Use the merged options to build and return a X.
// ...
}
The reason why the above implementation is not recommended is the
append
call may modify the underlying Option
sequence of the argument opts
.
For most scenarios, it is not a problem.
But for some special scenarios, it may cause some unexpected results.
Option
sequence of
the input argument, we should use the following way instead.
func NewX(opts ...Option) *X {
opts = append(opts[:len(opts):len(opts)], defaultOpts...)
// Use the merged options to build and return a X.
// ...
}
On the other hand, for the callers of the NewX
function,
it is not a good idea to think and rely on the NewX
function
will not modify the underlying elements of the passed slice arguments,
so it is best to pass these arguments with the three-index subslice form.
Another scenario at which we should use three-index subslice form is mentioned in this wiki article.
One drawback of three-index subslice forms is they are some verbose. In fact, I ever made a proposal to make it less verbose, but it was declined.
Please read this article for details.
We can assign a value of the custom defined type to a variable of type of the specified interface type to make sure the custom type implements the specified interface type, and more importantly, to show the custom type is intended to implement which interface types. Sometimes, writing docs in runnable code is much better than in comments.
package myreader
import "io"
type MyReader uint16
func NewMyReader() *MyReader {
var mr MyReader
return &mr
}
func (mr *MyReader) Read(data []byte) (int, error) {
switch len(data) {
default:
*mr = MyReader(data[0]) << 8 | MyReader(data[1])
return 2, nil
case 2:
*mr = MyReader(data[0]) << 8 | MyReader(data[1])
case 1:
*mr = MyReader(data[0])
case 0:
}
return len(data), io.EOF
}
// Any of the following three lines ensures
// type *MyReader implements io.Reader.
var _ io.Reader = NewMyReader()
var _ io.Reader = (*MyReader)(nil)
func _() {_ = io.Reader(nil).(*MyReader)}
Besides the above one, there are more compile-time assertion tricks.
Several ways to guarantee a constantN
is not smaller than another constant M
at compile time:
// Any of the following lines can guarantee N >= M
func _(x []int) {_ = x[N-M]}
func _(){_ = []int{N-M: 0}}
func _([N-M]int){}
var _ [N-M]int
const _ uint = N-M
type _ [N-M]int
// If M and N are guaranteed to be positive integers.
var _ uint = N/M - 1
One more way which is stolen from @lukechampine.
It makes use of the rule that duplicate constant keys can't appear in the same composite literal.
var _ = map[bool]struct{}{false: struct{}{}, N>=M: struct{}{}}
The above way looks some verbose but it is more general. It can be used to assert any conditions.
It can be less verbose but needs a little more (negligible) memory:
var _ = map[bool]int{false: 0, N>=M: 1}
Similarly, ways to assert two integer constants are equal to each other:
var _ [N-M]int; var _ [M-N]int
type _ [N-M]int; type _ [M-N]int
const _, _ uint = N-M, M-N
func _([N-M]int, [M-N]int) {}
var _ = map[bool]int{false: 0, M==N: 1}
var _ = [1]int{M-N: 0} // the only valid index is 0
var _ = [1]int{}[M-N] // the only valid index is 0
var _ [N-M]int = [M-N]int{}
The last line is also inspired by one of Luke Champine's tweets.
Ways of how to assert a constant string is not blank:type _ [len(aStringConstant)-1]int
var _ = map[bool]int{false: 0, aStringConstant != "": 1}
var _ = aStringConstant[:1]
var _ = aStringConstant[0]
const _ = 1/len(aStringConstant)
The last line is stolen from Jan Mercl's clever idea.
Sometimes, to avoid package-level variables consuming too much memory, we can put assertion code in a function declared with the blank identifier. For example,func _() {
var _ = map[bool]int{false: 0, N>=M: 1}
var _ [N-M]int
}
const MaxUint = ^uint(0)
const MaxInt = int(^uint(0) >> 1)
const Is64bitArch = ^uint(0) >> 63 == 1
const Is32bitArch = ^uint(0) >> 63 == 0
const WordBits = 32 << (^uint(0) >> 63) // 64 or 32
When a non-interface value is assigned to an interface value, a copy of the non-interface value will be boxed into the interface value. The copy cost depends on the size of the non-interface value. The larger the size, the higher the copy cost. So please try to avoid boxing large-size values into interface values.
In the following example, the costs of the latter two print calls are much lower than the former two.package main
import "fmt"
func main() {
var a [1000]int
// This cost of the two lines is high.
fmt.Println(a) // a is copied
fmt.Printf("Type of a: %T\n", a) // a is copied
// The cost of the two lines is low.
fmt.Printf("%v\n", a[:])
fmt.Println("Type of a:", fmt.Sprintf("%T", &a)[1:])
}
About value sizes of different types, please read value copy costs in Go.
Please read this article to get what is BCE and how well BCE is supported by the standard Go compiler now.
Here, another example is provided:package main
import (
"strings"
"testing"
)
func NumSameBytes_1(x, y string) int {
if len(x) > len(y) {
x, y = y, x
}
for i := 0; i < len(x); i++ {
if x[i] != y[i] {
return i
}
}
return len(x)
}
func NumSameBytes_2(x, y string) int {
if len(x) > len(y) {
x, y = y, x
}
if len(x) <= len(y) { // more code but more efficient
for i := 0; i < len(x); i++ {
if x[i] != y[i] { // bound check eliminated
return i
}
}
}
return len(x)
}
var x = strings.Repeat("hello", 100) + " world!"
var y = strings.Repeat("hello", 99) + " world!"
func BenchmarkNumSameBytes_1(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = NumSameBytes_1(x, y)
}
}
func BenchmarkNumSameBytes_2(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = NumSameBytes_2(x, y)
}
}
In the above example, function NumSameBytes_2
is more efficient than function NumSameBytes_1
.
The benchmark result:
BenchmarkNumSameBytes_1-4 10000000 669 ns/op
BenchmarkNumSameBytes_2-4 20000000 450 ns/op
Please note, there are many small improvements in each main release of the standard Go compiler (gc).
The trick used in the above example doesn't work for Go Toolchain versions earlier than 1.11.
And future gc versions may become smarter so that the trick will become unnecessary.
In fact, since gc v1.11, the bounds check for y[i]
has been eliminated
if x
and y
are slices, even if the above trick is not used.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.