全てのパッケージには、パッケージコメント(package 句の前にあるブロックコメント)を持つべき。マルチファイルパッケージの場合、パッケージコメントは1つのファイルにのみ存在する必要がある。
/*
Package regexp implements a simple library for regular expressions.
The syntax of the regular expressions accepted is:
regexp:
concatenation { '|' concatenation }
concatenation:
{ closure }
closure:
term [ '*' | '+' | '?' ]
term:
'^'
'$'
'.'
character
'[' [ '^' ] character-ranges ']'
'(' regexp ')'
*/
package regexp
パッケージが単純な場合、パッケージのコメントは簡潔にすることができる。
// Package path implements utility routines for
// manipulating slash-separated filename paths.
生成された出力は、固定幅フォントで表示されない場合もあるため、配置のために space を用いないでください。
gofmt
やgodoc
でフォーマット化して、期待通りの表示をされているか確認する必要がある。
エクスポートされ r た名前にはすべてドキュメントコメントを残すべき。最初の文は、宣言している名前で始まる一文の要約であるべき。
// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {
なぜなら、「Compile」という関数の名前を思い出せなくても、正規表現の解析関数を探しているので以下のように同時に関数名が表示され簡単に見つけ出せる。
$ go doc -all regexp | grep -i parse
Compile parses a regular expression and returns, if successful, a Regexp
MustCompile is like Compile but panics if the expression cannot be parsed.
parsed. It simplifies safe initialization of global variables holding
宣言をグループ化したものに、ドキュメントコメントを残すとその関連する定数または変数のグループを紹介できる。
// Error codes returned by failures to parse an expression.
var(
ErrInternal = errors.New( "regexp:internal error")
ErrUnmatchedLpar = errors.New( "regexp:unmatched'('")
ErrUnmatchedRpar = errors.New( "regexp:unmatched')'")
...
)
パッケージ名には、小文字の一語の名前をつけるべき。underscore や mixedCaps を付けるべきではない。なぜなら、パッケージを使用するすべての人がその名前を入力するため、簡潔である必要がある。衝突しても別の名前を使用することができるので問題ない。
また、パッケージ名はそのソースディレクトリの basename であるべき。src/encoding/base64
パッケージはencoding/base64
として import される。
また、パッケージ名を使用することでエクスポートされる名前の重複を防ぐことができる。例えば、ring.Ring
というコンストラクタの名前をring.NewRing
とせず、ring.New
と簡潔に書くことができる。
ゲッターの名前に Get
を入れるのは慣例でも必要でもない。セッター関数が必要な場合は、 Set
をつけるのが慣例。
owner:= obj.Owner()
if owner!= user {
obj.SetOwner(user)
}
慣習として、1 メソッドのインターフェースは、メソッド名に er
サフィックスを付ける。Reader、Writer、Formatter、CloseNotifier など。
Go では複数単語の名前を書く場合、アンダースコアではなく MixedCaps か mixedCaps を使うのが慣例。
C 言語と同様、Go はステートメントの最後にセミコロンを使用しますが、C 言語とは異なり、ソースには記述しない。レキサが自動的にセミコロンを挿入する。
break 文で switch を抜けることができる。ラベル移動も可能。s
Loop:
for n := 0; n < len(src); n += size {
switch {
case src[n] < sizeOne:
if validateOnly {
break
}
size = 1
update(src[n])
case src[n] < sizeTwo:
if n+1 >= len(src) {
err = errShortInput
break Loop
}
if validateOnly {
break
}
size = 2
update(src[n] + src[n+1]<<shift)
}
}
var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
関数の戻り値には名前付きパラメータを用いることができる。名前をつけると、関数が始まる時にその型のゼロ値で初期化される。
これはコードをより短く、より明確化するためのもので、いわばドキュメントである。
func ReadFull(r Reader, buf []byte) (n int, err error) {
for len(buf) > 0 && err == nil {
var nr int
nr, err = r.Read(buf)
n += nr
buf = buf[nr:]
}
return
}
defer 文は、defer を実行する関数が戻る直前に、実行される関数呼び出しをスケジュールする。これは、関数がどの経路で戻るかに関係なくリソースを解放しなければならないような状況に対処するためな方法に用いられる。
例としては、Mutex の Unlock や File の Close などがある。
Close のような関数への呼び出しを延期することには、2つの利点がある。
defer 文に渡される関数の引数は、defer 文を通る時に評価される(関数を抜けた時に評価されない)。つまり、defer 文の関数の引数などが後で値が変わる心配がないという利点がある。また、一つの関数内で複数の defer 文を呼び出せることを意味する。
func trace(s string)string {
fmt.Println( "entering:"、s)
return s
}
func un(s string){
fmt.Println( "leaving:"、s)
}
func a(){
defer un(trace ("a"))
fmt.Println( "in a")
}
func b(){
defer un(trace( "b"))
fmt.Println( "in b")
a()
}
func main(){
b ()
}
entering: b
in b
entering: a
in a
leaving: a
leaving: b
new
new
とmake
という allocation primitive がある。
new
はメモリを確保する関数ですが、メモリを初期化するのではなく、ゼロ値にする。つまり、new(T)
はゼロ値で初期化された T 型を確保し、そのアドレスを(*T 型)を返す。
&T{}
と同じこと。
var a *Test
はメモリを初期化するだけなので、a はnil
になる。
配列は(アドレスではなく)値である。そのため、関数の引数に配列を渡すとその関数は配列へのポインタではなく、配列を丸々コピーされたものを受け取る。
効率化するために配列のポインタを渡せば良い。
func Sum(a *[3]float64) (sum float64) {
for _, v := range *a {
sum += v
}
return
}
array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator
しかし、Go ではこのような方法は慣例的ではない。代わりにスライスを用いるべき。
2次元スライスを初期化する方法は二つある
// Allocate the top-level slice.
picture := make([][]uint8, YSize) // One row per unit of y.
// Loop over the rows, allocating the slice for each row.
for i := range picture {
picture[i] = make([]uint8, XSize)
}
// Allocate the top-level slice, the same as before.
picture := make([][]uint8, YSize) // One row per unit of y.
// Allocate one large slice to hold all the pixels.
pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.
// Loop over the rows, slicing each row from the front of the remaining pixels slice.
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}