Skip to content
Go言語とは、Googleが開発した新しいプログラミング言語です。
当サイトではこの新しい言語についての情報を集約していきます。
このサイトの主要コンテンツは、公式サイトの日本語訳です。右側のメニューから選んでください。

Archive

Archive for 12月, 2009

The Go Programming Language Specificationの翻訳、10回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


ステートメント

ステートメントは実行を制御します。

Statement =
	Declaration | LabeledStmt | SimpleStmt |
	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
	DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | IncDecStmt | Assignment | ShortVarDecl .

StatementList = Statement { Separator Statement } .
Separator     = [ ";" ] .

ステートメントリスト(StatementList)の要素はセミコロンで区切られます。ただし直前のステートメントが次のときだけはセミコロンは省略可能です。

  • 宣言リストが、閉じる丸括弧“)”で終わっているとき。もしくは、
  • 閉じる波括弧“}”で終わっていて、それが式の一部ではないとき。

空ステートメント

空ステートメントは、何も行いません。

EmptyStmt = .

空ステートメントを加えることで、ステートメントリストをいつでも実質的にセミコロンで終了させることができます。

ラベル付きステートメント

ラベル付きステートメントは、gotobreakcontinueステートメントの宛先となります。

LabeledStmt = Label ":" Statement .
Label       = identifier .
Error: log.Fatal("error encountered")

式ステートメント

関数呼び出し、メソッド呼び出し、チャネル操作は、ステートメントの文脈内に記述することができます。

ExpressionStmt = Expression .
f(x+y)
<-ch

インクリメント/デクリメントステートメント

“++”と“–”ステートメントは、型を持たない定数1を使って、オペランドをインクリメントまたはデクリメントします。代入を伴うときは、オペランドは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のいずれかでなければなりません。

IncDecStmt = Expression ( "++" | "--" ) .

以下の代入ステートメントは、同じ意味合いです。

Inc/Dec             代入
x++                 x += 1
x--                 x -= 1

代入

Assignment = ExpressionList assign_op ExpressionList .

assign_op = [ add_op | mul_op ] "=" .

左辺の各オペランドは、アドレス指定可能か、マップのインデックス式、ブランク識別子のいずれかでなければなりません。

x = 1
*p = f()
a[i] = 23
k = <-ch

代入操作 x op= yにおいて、opが二項算術演算子のとき、x = x op yと同等ですが、このときxが評価されるのは一度だけです。また、このop=はひとつのトークンを構成します。代入操作において、左辺、右辺双方の式リスト(ExpressionList)は、単一値となるひとつの式でなければなりません。

a[i] <<= 2
i &^= 1<<n

組み合わせ代入は、複数値となる操作の各要素を、変数リストに個別に代入します。これには2つの形式があります。1番目の形式では、右辺のオペランドは、関数の評価、チャネルマップ操作、型アサーションのような、ひとつの複数値となる式です。左辺のオペランドの数は、右辺の値の数と一致しなければなりません。例えば、fが2つの値を返す関数のときは、

x, y = f()

これは、1番目の値をxに、2番目の値をyに代入します。ブランク識別子を使うと、複数値となる式から返された値を無視することができます。

x, _ = f()  // f()から返される2番目の値を無視

2番目の形式では、左辺のオペランドの数は、右辺の式の数と一致し、かつ各右辺の式はすべて単一値とならなければなりません。右辺のn番目の式は、左辺のn番目のオペランドに代入されます。このとき右辺の式が評価されるタイミングは、左辺のオペランドの何れかに代入が行われるより前に行われます。でなければ評価の順序規則から外れてしまうためです。

a, b = b, a  // aとbを入れ替え

代入において、それぞれの値は代入先オペランドの型と代入の適合性を持たなければなりません。型を持たない定数をインタフェース型の変数へ代入するとき、その定数はboolintfloatstring型いずれかの値へ変換されます。どの型になるかは、値が論理値、整数、浮動小数点、文字列型定数のどれであるかに依存します。

ifステートメント

ifステートメントは、2つに分岐したロジックを論理値型の式の値に従って実行します。式の評価結果がtrueとなるときは、if側のロジックが実行されます。falseとなるときに、elseが記述されていれば、それが伴うロジックが実行されます。条件が記述されなかったときはtrueと記述したことと同等です。

IfStmt    = "if" [ SimpleStmt ";" ] [ Expression ] Block [ "else" Statement ] .
if x > 0 {
	return true;
}

シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。

if x := f(); x < y {
	return x;
} else if x > z {
	return z;
} else {
	return y;
}

switchステートメント

switchステートメントは、複数に渡る分岐の実行を行います。どの分岐を実行すべきか判断するため、式または型指定と、switch内のcaseとが比較されます。

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

switchステートメントには、式switchと型switchの2つの形式があります。式switchcaseには、switch式の値と比較するための式が含まれます。型switchswitchには、switch式にて特殊な形式で示された型と比較するための型が含まれます。

式switch

switchにおいては、switchの式が評価されたあと、case式(定数である必要はない)が、左から右へ、上から下へと評価されていきます。switch式と等しい最初のcaseが見つかると、それが伴うステートメントが実行され、それ以外のcaseはスキップされます。一致するcaseがないときにdefaultケースがあれば、そのステートメントが実行されます。defaultケースは最大でも1つまでしか記述できませんが、switchステートメント内のどこにでも記述することができます。式が記述されなかったときはtrueと記述したことと同等です。

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" [ StatementList ] .
ExprSwitchCase = "case" ExpressionList | "default" .

caseまたはdefault節内の最終ステートメントだけには、制御が次のcaseまたはdefault節の先頭ステートメントへと流れるべきであることを示す、fallthroughステートメント(§fallthroughステートメント)を記述することができます。これが記述されないときは、制御の流れはswitchステートメントの終わりへ移ります。

シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}

switch x := f(); {
case x < 0: return -x
default: return x
}

switch {  // 式が記述されていないので"true"として扱われる
case x < y: f1();
case x < z: f2();
case x == 4: f3();
}

型switch

switchは、値の代わりに型を比較します。その他の点では、式switchとほぼ同じです。型switchは、予約語typeを型名の代わりとして使った型アサーションの形式を持つ特殊なswitch式であることによって識別されます。caseはリテラルの型と、型アサーションの式が示す動的な型とを比較します。

TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" .
TypeCaseClause  = TypeSwitchCase ":" [ StatementList ] .
TypeSwitchCase  = "case" TypeList | "default" .
TypeList        = Type { "," Type } .

型スイッチガード(TypeSwitchGuard)には、省略形式による変数の宣言を含むことができます。この形式が使われるとき、各case/default節内でその変数が宣言されます。リスト(TypeList)内に型をひとつだけ指定したcase節では、この変数はcaseで指定した型を持ちます。型を複数指定したときは、変数は型スイッチガードの式が表す型となります。

caseに指定する型をnil事前宣言済み識別子)とすることもできます。このcaseは、型スイッチガード内の式がnilインタフェース値であるときに選択されます。

下は、interface{}型の値を返す関数fを使った、型switchです。

switch i := f().(type) {
case nil:
	printString("f() returns nil");
case int:
	printInt(i);  // i is an int
case float:
	printFloat(i);  // i is a float
case func(int) float:
	printFunction(i);  // i is a function
case bool, string:
	printString("type is bool or string");  // i is an interface{}
default:
	printString("don't know the type");
}

これは、下のように書き直すこともできます。

v := f();
if v == nil {
	printString("f() returns nil");
} else if i, is_int := v.(int); is_int {
	printInt(i);  // i is an int
} else if i, is_float := v.(float); is_float {
	printFloat(i);  // i is a float
} else if i, is_func := v.(func(int) float); is_func {
	printFunction(i);  // i is a function
} else {
	i1, is_bool := v.(bool);
	i2, is_string := v.(string);
	if is_bool || is_string {
		i := v;
		printString("type is bool or string");  // i is an interface{}
	} else {
		i := v;
		printString("don't know the type");  // i is an interface{}
	}
}

シンプルステートメント(SimpleStmt)が、型スイッチガードの直前にあるときは、型スイッチガードが評価されるより前にシンプルステートメントが実行されます。

fallthroughステートメントは、型switchにおいては許可されていません。

forステートメント

forステートメントは、ブロックの繰り返し実行を行います。繰り返しは、条件(Condition)、for節(ForClause)、range節(RangeClause)のいずれかによって制御されます。

ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .

最も単純な形式のforステートメントでは、条件の評価結果の論理値がtrueとなっている間、ブロックの実行を繰り返します。条件は繰り返しが行われる直前に毎回評価されます。条件が記述されなかったときはtrueと記述したことと同等です。

for a < b {
	a *= 2
}

for節(ForClause)によるforステートメントも条件(Condition)によってコントロールされますが、それに加えて、代入、インクリメント、デクリメントなどを行う初期化ステートメント(InitStmt)、またはポストステートメント(PostStmt)を記述できます。初期化ステートメントは省略形式による変数の宣言ですが、ポストステートメントはそうである必要はありません。

ForClause = InitStmt ";" [ Condition ] ";" PostStmt .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i < 10; i++ {
	f(i)
}

初期化ステートメントが空でなければ、繰り返しの初回に条件が評価される直前に一度だけ実行されます。ポストステートメントはブロックを実行した直後、(ブロックが実行されたときだけ)実行されます。for節の各要素は空にすることができますが、条件だけを記述した場合を除き、セミコロンの記述が必要です。条件が指定されなかったときはtrueと記述したことと同等です。

for cond { S() }    is the same as    for ; cond ; { S() }
for      { S() }    is the same as    for true     { S() }

range節によるforステートメントは、配列、スライス、文字列、マップ、チャネルから受信した値、これらの全エントリを使って繰り返しを行います。エントリ毎にまず、カレントのインデックスまたはキーをイテレーション変数に代入するか、もしくはカレントの「インデックスと要素のペア」または「キーと値のペア」をそれと対になるイテレーション変数に代入します。そのあとでブロックが実行されます。

RangeClause = ExpressionList ( "=" | ":=" ) "range" Expression .

range節の右側の式の型は、配列、スライス、文字列、マップ、配列へのポインタ、チャネルのどれかでなければなりません。チャネルのときを除いて、左辺の識別子リスト(ExpressionList)は、1~2個の式(代入と同じく、これらは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のどれか)でなければなりません。各繰り返しにおいて、1番目のイテレーション変数にセットされるのは文字列または配列またはスライスのインデックス、マップのキーのいずれかであり、2番目のイテレーション変数があれば、それにセットされるのは1番目の変数と対応する文字列、配列要素、マップの値です。配列またはスライスのインデックスの型(これは常にint)、要素の型、マップのキーおよび値の型は、対応するイテレーション変数に対して代入の適合性を持っていなければなりません。

文字列を扱うとき、range節は文字列内のユニコードポイントを繰り返します。連続する繰り返しの際、インデックス用変数にセットされる値は、文字列中のUTF-8エンコードされたコードポイントの連続したバイト列の先頭のインデックスであり、2番目のint型の変数にセットされる値は、それと対応するコードポイントの値です。繰り返しの最中に無効なUTF-8シーケンスが現れたときは、2番目の変数は0xFFFD(Unicode replacement character)となり、次の繰り返しのときに文字列内を1バイト進めます。

チャネルを扱うときは、識別子リスト(ExpressionList)には、識別子がひとつだけでなければなりません。ループはチャネルがクローズされるまでチャネル上に送られた値を受信し続けますが、チャネルがクローズされるまでは、送られたゼロ値は処理されません。

イテレーション変数が、range節(“:=”)によって宣言されるとき、この変数のスコープは、forステートメントの終了までです(§宣言とスコープ)。このときこれらイテレーション変数の型は、それぞれint型と配列要素型のセット、もしくはマップのキーと値のセットのどちらかとなります。イテレーション変数がforステートメント外で宣言されているとき、その変数の実行後の値は、最後に繰り返した状態です。

var a [10]string;
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6};

for i, s := range a {
	// iの型はint
	// sの型はstring
	// s == a[i]
	g(i, s)
}

var key string;
var val interface {};  // mの値の型は、valに対して代入の適合性がある
for key, val = range m {
	h(key, val)
}
// key == マップの繰り返し内で最後に現れたエントリのキー
// val == map[key]

繰り返し中に、まだ処理されていないマップエントリが削除されたときは、そのエントリが処理されることはありません。また繰り返し中に、マップエントリが追加されたときの動作は実装に依存しますが、ひとつのエントリが複数回処理されることはありません。

goステートメント

goステートメントは、独立した並列スレッドまたはゴルーチン(goroutine)として、関数またはメソッドの実行を同一アドレス空間で開始します。

GoStmt = "go" Expression .

この式(Expression)は、関数またはメソッドの呼び出しでなければなりませんが、通常の呼び出しとは異なり、プログラムは実行された関数の完了を待ちません。

go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true; }} (c)

selectステートメント

selectステートメントは通信可能な集合の中から、実行可能なものを選択します。switchステートメントに似ていますが、すべてのcaseで通信操作を行っている点が異なります。

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase = "case" ( SendExpr | RecvExpr) | "default" .
SendExpr =  Expression "<-" Expression .
RecvExpr =  [ Expression ( "=" | ":=" ) ] "<-" Expression .

selectステートメント内のすべての送信・受信式において、チャネル式(送信式の右側の式も含めて)は、上から下へと順番に評価されます。selectステートメントの結果としてcaseがひとつ以上実行可能となると、その内のひとつが選択され、それが伴う通信処理とステートメントが評価されます。どれも実行可能とならないとき、defaultケースがあれば実行されますが、defaultケースがないときは、通信のどれか1つが実行可能となるまで、ステートメントはブロックします。チャネルおよび送信式が複数回評価されることはありません。チャネルのポインタがnilのときは、selectステートメント内にそのcaseが存在しないことと同等ですが、送信のときは式だけは評価されます。

まず、selectステートメント内のすべてのチャネルおよび送信式が評価されてから、その評価の二次的作用が通信に対して起こります。

実行可能なcaseが複数あるときは、平等かつ公平に選択が行われ、実行する通信がひとつだけ決定されます。

受信のcaseには、省略形式による変数の宣言を使って新しい変数を宣言することもできます。

var c, c1, c2 chan int;
var i1, i2 int;
select {
case i1 = <-c1:
	print("received ", i1, " from c1\n");
case c2 <- i2:
	print("sent ", i2, " to c2\n");
default:
	print("no communication\n");
}

for {  // ランダムなビットシーケンスをcに送信
	select {
	case c <- 0:  // note: no statement, no fallthrough, no folding of cases
	case c <- 1:
	}
}

returnステートメント

returnステートメントは、それが記述されている関数の実行を終了し、必要であれば戻り値として単一または複数の値を呼び出し元に返します。

ReturnStmt = "return" [ ExpressionList ] .

戻り値を持たない関数のreturnステートメントは、戻り値を返してはいけません。

func no_result() {
	return
}

戻り値を持つ関数から戻り値を返すには、3通りの方法があります。

  1. 戻り値として単一値または複数値がreturnステートメントで明示的にリストされます。個々の式は単一値であり、かつ対応する関数の戻り値の型と、代入の適合性を持っていなければなりません。
    func simple_f() int {
    	return 2
    }
    
    func complex_f1() (re float, im float) {
    	return -7.0, -4.0
    }
  2. returnステートメントの式リストは、複数値を返す関数の(一回の)呼び出しです。これは関数から返されたそれぞれの値が、適切な型を持ったテンポラリの変数に代入され、その変数がreturnステートメントにリストされるかのように振る舞います。そのあとは、前のケースで説明した規則が当てはまります。
    func complex_f2() (re float, im float) {
    	return complex_f1()
    }
  3. 関数の戻り値のパラメータに名前をつけているときは、returnの式を空にすることができます(§関数型)。戻り値パラメータは、通常のローカル変数と同様に、その型のゼロ値(§ゼロ値)で初期化され、関数内で必要に応じて値の代入が行われます。returnステートメントは、これら変数に格納されている値を返します。
    func complex_f3() (re float, im float) {
    	re = 7.0;
    	im = 4.0;
    	return;
    }

breakステートメント

breakステートメントは、最も内側にあるforswitchselectステートメントの実行を終了します。

BreakStmt = "break" [ Label ].

ラベルが指定されているときは、そのラベルはforswitchselectステートメントのどれかを伴っていなければならず、breakステートメントによって、これらステートメントが終了します(§forステートメント、§switchステートメント、§selectステートメント)。

L: for i < n {
	switch i {
		case 5: break L
	}
}

continueステートメント

continueステートメントは、最も内側のforループのポストステートメントから次の繰り返しを開始します (§forステートメント)。

ContinueStmt = "continue" [ Label ].

オプションとして指定可能なラベルは、breakステートメントのラベルと同じです。

gotoステートメント

gotoステートメントは、指定したラベルを持つステートメントへ制御を移します。

GotoStmt = "goto" Label .
goto Error

gotoステートメントの実行が原因で、それまでスコープ外だった変数が、スコープ内に入るようなケースは許されません。たとえば、この例などが相当します。

goto L;  // BAD
v := 3;
L:

この例は、ラベルLへジャンプすることで、変数vの作成をスキップしてしまうため誤りです。

fallthroughステートメント

fallthroughステートメントは、式switch式switch)の次のcase節の先頭のステートメントへ制御を移します。このfallthroughステートメントが記述できるのは、式switch内のcaseまたはdefault節の、空ではない最終ステートメントだけです。

FallthroughStmt = "fallthrough" .

deferステートメント

deferステートメントは、deferステートメントを記述している関数自体が復帰するまでの間、指定した関数の実行を先延ばしします。

DeferStmt = "defer" Expression .

deferに指定する式は、関数またはメソッドの呼び出しでなければなりません。deferステートメントが実行される度に、関数呼び出しのパラメータは評価され、評価結果が保存されますが、関数の実行は行われません。保留された関数呼び出しは、deferステートメントを記述している関数から復帰する直前(戻り値があれば、その評価後)に、LIFOの順序で実行されます。

lock(l);
defer unlock(l);  // この関数から復帰する直前にunlockが行われる

// この関数から復帰する直前に、3 2 1 0と出力される
for i := 0; i <= 3; i++ {
	defer fmt.Print(i);
}

The Go Programming Language Specificationの翻訳、9回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


式は、値の算出方法を規定します。値の算出はオペランドに演算子および関数を適用することで行われます。

オペランド

オペランドは、式の基本要素である値です。

Operand    = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" .
Literal    = BasicLit | CompositeLit | FunctionLit .
BasicLit   = int_lit | float_lit | char_lit | StringLit .

限定付き識別子

限定付き識別子とは、パッケージ名をプレフィックスとして指定した識別子で、これにはブランク識別子は使用できません。

QualifiedIdent = [ PackageName "." ] identifier .

限定付き識別子は、別パッケージの識別子にアクセスするときに使用します。その識別子はエクスポートされていなければなりません。すなわち識別子がユニコードの大文字で始まっている必要があります。

math.Sin

複合リテラル

複合リテラルは構造体、配列、スライス、マップを構築し、評価をその都度行って新しい値を作成します。複合リテラルは、値の型と、それに続く波括弧{}でくくられた要素リストから構成されます。この要素は単一式、もしくはキーと値のペアのどちらかです。

CompositeLit  = LiteralType "{" [ ElementList ] "}" .
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                SliceType | MapType | TypeName | "(" LiteralType ")" .
ElementList   = Element { "," Element } [ "," ] .
Element       = [ Key ":" ] Value .
Key           = FieldName | ElementIndex .
FieldName     = identifier .
ElementIndex  = Expression .
Value         = Expression .

このLiteralTypeは、構造体、配列、スライス、マップ型のいずれかでなければなりません(文法上、型がTypeNameと記述されたとき以外はこの制約が適用されます)。式の型は、LiteralTypeの各フィールド、または要素、またはキーの型との間で代入の適合性を持たなければなりません。このとき変換はできません。
キーは、構造体リテラルのフィールド名、配列またはスライスリテラルのインデックス式、マップリテラルのキーのいずれかとして解釈されます。マップリテラルのときは、全ての要素に対しキーを記述しなくてはなりません。また複数の要素に同じフィールド名やキー値を指定したときはエラーとなります。

構造体リテラルには次の規則が適用されます。

  • キーはLiteralTypeで宣言されているフィールド名でなければなりません。
  • リテラルにキーが含まれないときは、各フィールドの要素をフィールドが宣言されている順にリストしなければなりません。
  • キーを持つ要素がひとつでもあるなら、全ての要素にキーを持たせなければなりません。
  • リテラルにキーが含まれているときは、構造体の全フィールドに要素を持たせる必要はありません。省略されたフィールドはゼロ値となります。
  • リテラルの要素リストは省略可能です。このようなリテラルはその型のゼロ値となります。
  • 他のパッケージに属している構造体の非エクスポートフィールドに要素を設定しようとするとエラーとなります。

構造体を定義します。

type Point struct { x, y, z float }
type Line struct { p, q Point }

次のように記述します。

origin := Point{};                            // Pointはゼロ値
line := Line{origin, Point{y: -4, z: 12.3}};  // line.q.xはゼロ値

配列リテラル、スライスリテラルには次の規則が適用されます。

  • 各要素は、配列内の位置を示す整数インデックスを持ちます。
  • キーを伴った要素は、そのキーをインデックスとして使用します。キーは整数の定数式でなければなりません。
  • キーを伴わない要素は、前の要素のインデックスを+1した値をインデックスとして用います。先頭の要素がキーを伴わないときは、そのインデックスはゼロです。

複合リテラルのアドレスを取得(§アドレス演算子)すると、リテラル値のインスタンスを指すユニークなポインタが生成されます。

var pointer *Point = &Point{y: 1000};

配列リテラルの長さは、LiteralType内で指定した長さです。要素数がリテラルで指定した長さに足りないとき、不足した要素にはその要素型のゼロ値がセットされます。配列のインデックスの範囲を超えたインデックス値を要素に指定するとエラーとなります。配列の長さに…と記述すると、要素の最大インデックス値に+1した値を指定したことと同じになります。

buffer := [10]string{};               // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5};         // len(intSet) == 6
days := [...]string{"Sat", "Sun"};    // len(days) == 2

スライスリテラルは、元になっている配列リテラル全体を表します。そのためスライスの長さとキャパシティは、要素の最大インデックス値に+1した値となります。スライスリテラルは次の形式です。

[]T{x1, x2, ... xn}

そして次は、配列リテラルに対してスライス操作を行うショートカットです。

[n]T{x1, x2, ... xn}[0 : n]

“if”、”for”、”switch”ステートメントの条件内に、LiteralTypeとしてTypeName形式を使った複合リテラルが現れると、意味が曖昧になり構文解析に支障をきたします。これはリテラル内の波括弧{}でくくられた式と、これらのステートメントに続くステートメントブロックとの見分けがつかないためです。この稀なケースにて発生する曖昧さを解決するには、複合リテラルを丸括弧()内に記述しなければなりません。

if x == (T{a,b,c}[i]) { ... }
if (x == T{a,b,c}[i]) { ... }

次は正しく配列、スライス、マップリテラルを使った例です。

// 素数リスト
primes := []int{2, 3, 5, 7, 9, 11, 13, 17, 19, 991};

// chが母音(vowel)のとき、vowels[ch]の値はtruevowels[ch]
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true};

// 配列 [10]float{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1};
filter := [10]float{-1, 4: -0.1, -0.1, 9: -1};

// 平均律音階の周波数(Hz) (A4 = 440Hz)
noteFrequency := map[string]float{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

関数リテラル

関数リテラルは匿名関数を表します。匿名関数は関数の型および関数の本体から構成されます。

FunctionLit = FunctionType Body .
func (a, b int, z float) bool { return a*b < int(z) }

関数リテラルは変数に代入することも、直接実行することも可能です。

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK } (reply_chan)

関数リテラルはクロージャです。そのため関数リテラル内から、外側の関数内で定義した変数を参照可能です。これらの変数は外側の関数と、関数リテラル間で共有され、これらからアクセス可能な限り存続します。

基本式

基本式は、単項式、二項式に与えられるオペランドです。

PrimaryExpr =
	Operand |
	Conversion |
	BuiltinCall |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Call .

Selector       = "." identifier .
Index          = "[" Expression "]" .
Slice          = "[" Expression ":" Expression "]" .
TypeAssertion  = "." "(" Type ")" .
Call           = "(" [ ExpressionList ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
Math.sin
f.p[i].x()

セレクタ

次の形式の基本式があります。

x.f

これは、x(またはxがポインタ型であれば*x)で表される値が持つ、フィールドまたはメソッドfを表します。識別子fは(フィールドまたはメソッドの)セレクタと呼ばれます。セレクタはブランク識別子であってはなりません。この式の型はfの型です。

セレクタfは、型Tのフィールド/メソッドfを表すか、もしくはT内でネストしている匿名フィールドのフィールド/メソッドfを表します。fに到達するまで渡り歩いた匿名フィールドの数は、Tにおけるfの深さと呼ばれます。Tに直接宣言されていればフィールド/メソッドfの深さはゼロです。T内の匿名フィールドAで宣言されていればフィールド/メソッドfの深さは、Aの深さ+1となります。

セレクタには次の規則が適用されます。

  1. 仮に、型Tもしくは*Tである値xがあり、Tがインタフェース型でなく、該当するフィールドまたはメソッドfが存在するならば、x.fが表すのは、T内で深さの値が最も小さいフィールドまたはメソッドfです。最も浅い深さのfがひとつだけでなければ、このセレクタは不正となります。
  2. 仮に、型Iもしくは*Iである値xがあり、Iがインタフェース型で、かつ該当するメソッドが存在するならば、x.fが表すのは、xに割り当てられている値が持っているfという名前の実メソッドです。xに値がないかnilのとき、x.fは不正となります。
  3. これら以外のケースでは、x.fは不正となります。

セレクタは自動的にポインタの間接参照を行います。xがポインタ型のとき、x.y(*x).yの簡略形として使用可能です。yも同じくポインタ型のとき、x.y.zも同様に(*(*x).y).zの簡略形です。
ただし、*xがポインタ型のときは、明示的に間接参照を行わなければなりません。これは自動間接参照が行われるのは1レベルだけだからです。例えば、T型の値x*Aとして宣言された匿名フィールドを含んでいるとき、x.f(*x.A).fの簡略形です。

例文のために、宣言を行います。

type T0 struct {
	x int;
}

func (recv *T0) M0()

type T1 struct {
	y int;
}

func (recv T1) M1()

type T2 struct {
	z int;
	T1;
	*T0;
}

func (recv *T2) M2()

var p *T2;  // with p != nil and p.T1 != nil

次のように記述します。

p.z         // (*p).z
p.y         // ((*p).T1).y
p.x         // (*(*p).T0).x

p.M2        // (*p).M2
p.M1        // ((*p).T1).M1
p.M0        // ((*p).T0).M0

インデックス

次の形式の基本式があります。

a[x]

これは、配列、スライス、文字列、マップa内の、xでインデックス指定された要素を表します。このxの値は、インデックスまたはマップのキーと呼ばれます。これには次の規則が適用されます。

仮にaが、配列型の型Aまたは*A、もしくはスライス型の型Sの値であるとします。

  • xは整数値で、0 <= x < len(a)でなければならない
  • a[x]はインデックスxの位置にある配列要素であり、a[x]の型はAの要素型である

仮にaが、文字列型である型Tの値であるとします。

  • xは整数値で、0 <= x < len(a)でなければならない
  • a[x]はインデックスxの位置にあるバイトであり、a[x]の型はbyte型である
  • a[x]には値を代入できない

仮にaが、マップ型である型Mの値であるとします。

  • xの型は、Mのキーの型と互換性を持つ必要があり、かつこのマップはxをキーとするエントリを持っていなければならない
  • a[x]は、マップ内のxをキーとする値であり、a[x]の型はMの値の型である

これ以外のa[x]は不正となります。また、インデックスまたはキーが範囲外であるときは、たとえインデックス式としては正しくても、ランタイム例外が発生します。

ただし、インデックス式がマップであるときは、次の形式を使ってmap[K] V型であるマップaから代入または、変数の初期化を行えます。

r, ok = a[x]
r, ok := a[x]
var r, ok = a[x]

このインデックス式の結果として、(V, bool)型の2つの値が返されます。指定したキーがマップ内に存在したときは、この式は(a[x], true)を返します。存在しない時は、(Z, false)を返します。このZV型のゼロ値です。このときは、ランタイム例外は発生しません。上の例のように、このときのインデックス式は、値と成否を返すような関数を呼び出したのと同様に振舞います。 (§代入)

同様に、マップに代入するときは、次の特殊な形式を使うことができます。

a[x] = r, ok

このとき、論理値であるokの値がfalseであれば、キーがxであるエントリはマップから削除されます。okの値がtrueであれば、通常通りマップに要素が代入されます。

スライス

文字列、配列、またはスライス自身をスライスすることで、部分文字列、または部分配列の参照を作ることができます。スライスを行うときは、結果として得たい要素をインデックス式で選択します。得られる結果は0から始まるインデックスを持ち、長さはスライスするときに指定した2つのインデックス値の差と等しくなります。次は配列aのスライスです。

a := [4]int{1, 2, 3, 4};
s := a[1:3];

このスライスsの型は[]intであり、長さは2、キャパシティは3です。要素の値は次となります。

s[0] == 2
s[1] == 3

スライスの長さはマイナス値にはなりません。また配列または文字列のスライス作成時のインデックス[lo:hi]が、「 0 <= lo <= hi <= 長さ」を満たしていなくてはなりません。スライスからスライスを作成するときは、上限値は長さではなくキャパシティとなります。

スライスする対象が、文字列またはスライスのとき、スライスの結果は文字列もしくは同じ型のスライスになります。しかしスライスの対象が配列のときは、スライスの結果は、その配列の要素型と同じ要素型を持ったスライスになります。

型アサーション

xおよび型Tがあると仮定して、次の基本式をみてください。

x.(T)

この式は、xはゼロ値ではなく、かつxにはT型の値が格納されていると断定します。このx.(T)という表記は、型アサーションと呼ばれます。このときのxの型はインタフェース型でなければなりません。

より明確にすると、Tがインタフェース型でないときx.(T)は、xの動的な型とTが同一の型であることを表しています。(§型の同一性と互換性)。もし、Tがインタフェース型のときx.(T)は、xがインタフェースTを実装している動的な型であることを表します(§インタフェース型)。

型アサーションが有効であれば、その式が表す値は、xに格納されている型Tの値となります。ただし型アサーションに失敗したときはランタイム例外が発生します。これらを言い換えると、正しいプログラムにおいては、xの動的な型が実行時にしか分からなくともx.(T)Tになりうることだけは分かっているということです。

型アサーションが代入、または初期化で使われるときは次の形式になります。

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)

このアサーションの結果として、(T, bool)型の2つの値が返されます。アサーションに成功したときは、この式は(x.(T), true)を返します。失敗したときはこの式は(Z, false)を返します。このZT型のゼロ値です。このときは、ランタイム例外は発生しません。上の例のように、このときの型アサーションは、値と成否を返すような関数を呼び出したのと同様に振舞います。 (§代入)

呼び出し

下は、F型の関数であるfの式です。

f(a1, a2, ... an)

これは引数、a1, a2, ... anを伴なうfの呼び出しです。1つの特例を除いて、各引数は単一値となる式であり、その値はFのパラメータの型と代入の適合性を持つ必要があります。これらの引数の式は関数の呼び出し前に評価されます。この式の型は、Fの戻り値の型となります。メソッドの実行も同様ではありますが、メソッドは、そのメソッドのレシーバの型の値に対するセレクタとして指定されます。

Atan2(x, y)    // 関数の呼び出し
var pt *Point;
pt.Scale(3.5)  // レシーバptによるメソッドの呼び出し

特例として、関数またはメソッドgの戻り値と、別の関数またはメソッドであるfの各パラメータの数およびそれらの代入の適合性が一致していれば、f(g(parameters_of_g))の呼び出しによって、gの戻り値をfのパラメータとして順に代入したのち、fが実行されます。ただし、fの呼び出しにはgからの戻り値以外のパラメータを指定することはできません。またfの最後のパラメータが…のときは、fの戻り値のうち通常の代入を行った残りがそこに代入されます。

func Split(s string, pos int) (string, string) {
	return s[0:pos], s[pos:len(s)]
}

func Join(s, t string) string {
	return s + t
}

if Join(Split(value, len(value)/2)) != value {
	log.Fatal("test fails")
}

メソッド呼び出しx.m()は、x(の型)のメソッド群がmを含んでいて、かつ引数リストがmの引数リストと代入の適合性があるときに有効となります。またxアドレス指定可能であり、&xのメソッド群がmを含んでいるならば、x.m()は、(&x).m()の簡略形として使用可能です。

var p Point;
p.Scale(3.5)

これ以外のメソッド型やメソッドリテラルはありません。

…パラメータの解析

関数fが…パラメータを持つとき、…は常に一番最後の仮パラメータとなります。fの呼び出しのとき、…より前の引数は通常通り扱われます。それらのパラメータのあとに続いて現れた任意数(ゼロも含む)の引数が…パラメータにバインドされます。

f関数内では…パラメータは、静的な型interface{} (空インタフェース)を持ちます。
各呼び出しにおいて、このパラメータの動的な型は、呼び出し時に並べられた引数を連続したフィールドとして持つ構造体となります。つまり、…に与えられた実引数が構造体にラップされて、実引数の代わりとして渡されます。リフレクションインタフェースを使用すると、この動的な型から要素を取り出して、本来の実引数を得ることができます。

関数とその呼び出しです。

func Fprintf(f io.Writer, format string, args ...)
Fprintf(os.Stdout, "%s %d", "hello", 23);

このFprintf呼び出しにおいて、このargsの動的な型は概念的に、struct { string; int }となります。

特例として、関数が受け取った…パラメータを、別の関数を呼び出す際に…パラメータとして使用するときは、このパラメータは再ラップされることなくそのまま渡されます。すなわち、…仮パラメータは変更されることなく…実パラメータとして受け渡されます。

演算子

演算子はオペランドを伴って式を作ります。

Expression = UnaryExpr | Expression binary_op UnaryExpr .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = log_op | com_op | rel_op | add_op | mul_op .
log_op     = "||" | "&&" .
com_op     = "<-" .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

比較演算子については別途説明します。それ以外の二項演算子では、チャネル、シフト、型を持たない定数のいずれかを伴う演算子を除き、オペランドの型は同じでなければなりません (§型と値の特性)。定数のみを伴う演算子については、定数式のセクションを参照ください。

チャネルの送信のときは、最初のオペランドは常にチャネルであり、2番目のオペランドは、チャネルの要素型に対して代入の適合性を持つ値でなくてはなりません。

シフト演算を除き、一方のオペランドが型を持たない定数で、もう一方がそれ以外のときは、定数のオペランドが相手側のオペランドの型に変換されます。

シフト演算の右側のオペランドは符号なし整数型か、もしくは符号なし整数型に変換可能で型を持たない定数でなければなりません。

定数とならないシフト演算の場合で、左側オペランドが型を持たない定数であるときは、その定数の型はシフト演算自体を左側オペランドひとつと置き換えてみたときに得られる型となります。

var s uint = 33;
var i = 1<<s;          // 1はint型
var j = int32(1<<s);   // 1はint32型で、j == 0
var u = uint64(1<<s);  // 1はint64型で、u == 1<<33
var f = float(1<<s);   // 不正。1はfloat型で、シフト不可
var g = float(1<<33);  // 正しい。1<<33はシフト演算の定数で、g == 1<<33

演算子の優先順位

単項演算子は高い優先順位を持っています。++--演算子は、式ではなくステートメントを構成するため演算子のグループからは除外されています。そのためステートメント*p++は、(*p)++と同じです。

二項演算子には、6つの優先順位レベルがあります。乗算演算子は最も強く、それに続いて加算演算子、比較演算子、<- (チャネル送信)、&&(論理積)、最後が ||(論理和)です。

優先順位         演算子
    6             *  /  %  <<  >>  &  &^
    5             +  -  |  ^
    4             ==  !=  <  <=  >  >=
    3             <-
    2             &&
    1             ||

同じ優先順位を持つ二項演算子は、左から右へと対応づけされます。例で示すと、x / y * zは、(x / y) * zと同じになります。

+x
23 + 3*x[i]
x <= f()
^a >> b
f() || g()
x == y+1 && <-chan_ptr > 0

算術演算子

算術演算子は数値に対して使用します。その算出結果の型は一つ目のオペランドの型と同じになります。四則演算子(+, -, *, /)は、整数及び浮動小数点に対して使用しますが、+は文字列にも使います。その他の算術演算子は整数にのみ使います。

+    和                     整数、浮動小数点、文字列
-    差                     整数、浮動小数点
*    積                     整数、浮動小数点
/    商                     整数、浮動小数点
%    剰余                   整数

&    ビット演算 and          整数
|    ビット演算 or           整数
^    ビット演算 xor          整数
&^   ビットクリア(and not)   整数

<<   左シフト                整数 << 符号なし整数
>>   右シフト                整数 >> 符号なし整数

文字列は、+演算子、または+=代入演算子を使用して連結することができます。

s := "hi" + string(c);
s += " and good bye";

文字列の加算は、オペランドを連結することで新たな文字列を作り出します。

整数型のとき、/%は以下の関係を満たします。

(a / b) * b + a % b == a

(a / b)は、ゼロに近づくように切り捨て/切り上げられます。例を上げますと、

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

被除数が正の値で、除数が2の累乗の定数であるときは、その割り算は右シフトに置き換えられ、剰余の計算はビット演算のANDに置き換えられます。

 x     x / 4     x % 4     x >> 2     x & 3
 11      2         3         2          3
-11     -2        -3        -3          1

シフト演算子は、右オペランドで指定されたシフト数、左オペランドをシフトします。実装としては、シフトの左オペランドが、符号あり整数のとき算術シフト、符号なし整数のときは論理シフトが使われます。シフト数は符号なし整数でなければなりません。また、シフト数には上限がありません。シフト数nでシフトを行うとき、シフト演算は左オペランドをn回繰り返して、シフト数1でシフトしたように振舞います。シフト演算の結果として、x << 1x*2と同じであり、x >> 1x/2を負の無限大の値に近づくように切り捨てた値と同じになります。

整数オペランドに対する単項演算子+-^は以下で示すように定義されています。

+x                          は、0 + x
-x    符号反転               は、0 - x
^x    ビットの補数           は、m ^ x (xが符号なしのとき、mの全ビットは1。
                                      xが符号ありのとき、mは-1)

浮動小数点においては、+xxと同じであり、-xxの符号を反転させた値です。

整数のオーバフロー

符号なし整数において、+-*<<演算子はmodulo 2n (nは、この符号なし整数型のビット幅)で計算されます(§数値型)。大雑把な解説をすると、これら符号なし整数の演算は、オーバフローした高位のビットを破棄するので、プログラム側はこの「ラップアラウンド」が行われることを期待してもよいでしょう。

符号あり整数において、+-*<<演算子はオーバフローを起こすことがあり、演算結果の値として何が返されるかは、この符号あり整数の値、演算子、オペランドにより決まります。オーバフローが起きても例外は発生しません。オーバフローは起こらないという前提のため、コンパイラはこれに関するコードの最適化は行いません。たとえば、x < x + 1が常に成り立つとは限りません。

比較演算子

比較演算子は、bool型の値を返します。演算子==!=は、いくつかのケースを除いて配列と構造体以外のすべての型に適用できます。他の比較演算子に適用できるのは数値と文字列だけです。

==    等しい
!=    等しくない
<     小なり
<=    小なりイコール
>     大なり
>=    大なりイコール

数値型のオペランドは、一般的な方法で比較されます。

文字列型のオペランドは、バイト単位で(辞書的に)比較されます。

論理値型のオペランドは、双方がtrue 、または双方がfalseのときに等しいとみなされます。

複合型の比較の規則は、§比較の適合性にて解説します。

論理演算子

論理演算子は論理値に適用され、オペランドと同じ型で結果を返します。右オペランドが評価されるかどうかは条件によります。

&&    and条件    p && q  is  "if p then q else false"
||    or条件     p || q  is  "if p then true else q"
!     否定       !p      is  "not p"

アドレス演算子

アドレス演算子&は、オペランドのアドレスを生成します。このときオペランドはアドレス指定可能でなければならず、また変数、ポインタの間接参照、配列またはスライスのインデックス操作、アドレス指定可能な構造体のフィールドセレクタのいずれかでなければなりません。関数の戻り値はアドレス指定可能ではありません。オペランドとしてポインタ型を取るポインタの間接参照演算子*は、オペランドによって指し示されている値を取り出します。

&x
&a[f(2)]
*p
*pf(x)

通信演算子

チャネルという用語は「チャネル型の値」を意味します。

送信操作には、二項演算子“<-”を使用します。この演算子はチャネルと値(式)に作用します。

ch <- 3

送信操作によって、チャネル上に値を送信します。チャネルと式は通信を開始する前に評価されます。通信は、送信が実行可能となるまでブロックされ、可能になると値はチャネルに送られます。バッファリングされていないチャネルへの送信は、受信側の準備ができているときに実行可能です。バッファリングされているチャネルへの送信は、バッファに空きがあるときに実行できます。

送信操作が、式のコンテキスト中に現れるならば、その式の値は論理値であり、操作はブロックされません。通信が行われたときは、その論理値の値はtrueとなります。行われなかったときはfalseになります。(成否とは関係なく、チャネルと送信される式は評価が行われます。)
次の2つの例は等しい内容です。

ok := ch <- 3;
if ok { print("sent") } else { print("not sent") }

if ch <- 3 { print("sent") } else { print("not sent") }

言い換えるなら、プログラムが送信操作の値をチェックするときは、送信はブロックされず、その式の値は操作の結果となります。プログラムで値をチェックしなければ、それが行われるまで操作はブロックし続けます。

受信操作には、単項演算子“<-”を使用します。この式の値は受信した値であり、その型はこのチャネルの要素型です。

<-ch

値が利用可能になるまで式はブロックされ、そのあとは変数に代入するなど、他の式と同じように利用できます。受信した値を受け取らなければ、その値は破棄されます。

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // クロックパルス待ち

受信式が代入、または初期化で使われるときは次の形式になります。

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch

この受信操作のときはブロックされません。受信操作が実行できるときは、論理値型の変数oktrue が、xには受信した値が格納されます。そうでなければokにはfalse がセットされ、xにはその型のゼロ値がセットされます。(§ゼロ値

メソッド式

メソッドMが、型Tのメソッド群に含まれているとき、Mの引数リストの先頭にこのメソッドのレシーバを加えることで、 T.Mを普通の関数として呼び出すことができます。

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = TypeName | "(" "*" TypeName ")" .

2つのメソッド、Mv(レシーバは型 T)とMp(レシーバは型*T)を持つ構造体型Tについて考えてみます。

type T struct {
	a int;
}
func (tv  T) Mv(a int)   int   { return 0 }  // 非ポインタのレシーバ
func (tp *T) Mp(f float) float { return 1 }  // ポインタのレシーバ
var t T;

次の式をみてください。

T.Mv

これは、Mvメソッドとは等しいですが、必ず引数の先頭にレシーバを持った関数です。この関数のシグネチャは、次のようになります。

func (tv T, a int) int

この関数は明示的にレシーバを指定することで、通常通り呼び出すことができます。下の3つの呼び出しは等価です。

t.Mv(7)
T.Mv(t, 7)
f := T.Mv; f(t, 7)

次の同じような式をみてください。

(*T).Mp

これは、Mpメソッドを表す関数値であり、次のシグネチャを持っています。

func (tp *T, f float) float

非ポインタのレシーバを持つメソッドから、ポインタのレシーバを持った関数を得ることができます。これは次のようになります。

(*T).Mv

これは、Mv メソッドを表す関数値であり、次のシグネチャを持っています。

func (tv *T, a int) int

このような関数では、本来のメソッドのレシーバへ受け渡す値を得るためにレシーバを間接参照します。メソッドは、関数呼び出しで渡されたこのアドレスの値を上書きすることはありません。

最後のケースとして、ポインタのレシーバを持つメソッドを、非ポインタのレシーバ関数として使用することはできません。なぜならポインタのレシーバを持つメソッドは、この型のメソッド群には含まれていないからです。

メソッドから取得した関数の値は、関数の呼び出し構文によって呼び出されます。このときレシーバは、呼び出しの引数の先頭に与えられます。つまり、f := T.Mvのとき、ft.f(7)ではなく、 f(t, 7)として実行します。レシーバにバインドする関数を作成するには、クロージャを使ってください。

インタフェース型のメソッドから関数の値を引き出すこともできます。結果得られる関数は、そのインタフェース型のレシーバを必ず受け取ります。

変換

変換とは、Tが型であり、xが型Tへ変換可能な式であるとき、T(x)で表される式です。

Conversion = LiteralType "(" Expression ")" .

一般的に変換は、xの値が型T代入の適合性を持っているとき、あるいは値と型Tが代入の整合性をとりうるとき、あるいは値の型、T、またはこれらのコンポーネント型が名前を持たないときに成功します。通常、このような変換は、xの値でなく型を変更するため、実行時にはコストがかかりません。

変換規則は、Tが数値型または文字列型である変換に適用されます。これらの変換によって値の内容が変わったり、実行コストが増えたりする可能性があります。

整数型間の変換

値が符号を持った数値であるならば、無限精度まで暗黙的に符号拡張されます。さもなければ、その値はゼロ拡張されます。そのあとで変換結果となる型に合うように精度が切り捨てられます。たとえば、x := uint16(0x10F0)ならば、uint32(int8(x)) == 0xFFFFFFF0になります。変換の結果は常に有効な値となり、オーバフローは起こしません。

浮動小数点型を含む変換

  1. 浮動小数点数を整数に変換するとき、小数部は捨てられます(ゼロに近づくよう切り捨て/切り上げ)。
  2. 数値を浮動小数点型に変換するとき、結果となる値はその浮動小数点型で規定されている精度に丸められます。たとえば、float32型の変数xの値は、格納されるときIEEE-754 32ビットの数値以上の精度が使われます。しかし、float32(x)が表すのは32ビットに丸められたxの値です。同様に、 x + 0.1は32ビット以上の精度が使われますが、float32(x + 0.1)はそうなりません。

浮動小数点値を含んでいるすべての変換において、結果となる型が値を表現することができなければ、変換自体は成功しますが、結果となる値は実装依存となります。

文字列への変換

  1. 整数値を変換することで、その整数が表すUTF-8文字を持つ文字列が得られます。
    string(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
  2. 整数のスライスを変換することで、各整数を文字列へ変換したあと結合した文字列が得られます。スライスの値がnilであれば、結果は空文字列になります。
    string([]int{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
  3. バイトのスライスを変換することで、スライス内の連続したバイトデータをそのまま持った文字列が得られます。スライスの値がnilであれば、結果は空文字列になります。
    string([]byte{'h', 'e', 'l', 'l', 'o'})  // "hello"

ポインタと整数間で変換を行う仕組みは言語上はありません。ただしunsafeパッケージでは、ある程度の制限はありますがこの機能を実装しています。

定数式

定数式は、定数オペランドだけを含み、コンパイル時に評価される式です。

論理値型、数値型、文字列型定数がオペランドとして合法的に使えるときは常に、型を持たない論理値型、数値型、文字列型定数をそれぞれオペランドとして使用できます。シフト演算を除き、二項演算のオペランドが型を持たない整数定数と、同じく型を持たない浮動小数点定数であるとき、整数定数は型を持たない浮動小数点定数に変換されます(/%がこれに相当します)。

結果がbool型となる比較演算子を除き、型を持たない定数に演算子を適用した結果は、同種(すなわち、論理値型、整数型、浮動小数点型、文字列型定数いずれか)の型を持たない定数となります。

定数式は、常に正確に評価されます。そのため評価中の値と定数は、言語でサポートされている事前定義済みの型よりかなり大きな精度を必要とするかもしれません。よって以下は、正しい宣言です。

const Huge = 1 << 100;
const Four int8 = Huge >> 98;

型を持っている定数の値は常に、その定数の型の値を正確に表せなければなりません。よって以下の定数式は正しくありません。

uint(-1)       // -1はuintでオーバフローを起こす
int(3.14)      // 3.14はintegerで切り捨てられる
int64(Huge)    // 1<<100はint64でオーバフローを起こす
Four * 300     // 300はint8でオーバフローを起こす
Four * 100     // 400はint8でオーバフローを起こす

単項のビット補数演算子^を使用したマスクは、非定数の規則と適合します。このマスクは符号なし定数のときは全て1に、符号あり、または型を持たない定数のときは-1になります。

^1          // 型を持たない整数定数。-2に等しい
uint8(^1)   // エラー。 uint8(-2)と同じで、範囲外
^uint8(1)   // uint8型の定数。0xFF ^ uint8(1) = uint8(0xFE)となる
int8(^1)    // int8(-2)と同じ
^int8(1)    // -1 ^ int8(1) = -2となる

評価の順番

代入または式の要素を評価するときには、全ての関数呼び出し、メソッド呼び出し、通信操作は、字句ごとに左から右へと順に評価されます。

代入例です。

y[f()], ok = g(h(), i() + x[j()], <-c), k()

これら関数の呼び出しと通信は、f()h()i()j()<-cg()k()の順で起こります。しかし、これら関数呼び出しと通信の順番は、xのインデックス指定と評価、およびyの評価と比べると未定義です。

単一式中の浮動小数点演算は、演算子がもつ結合法則に従って評価されます。明示的な括弧は、規定の結合法則を上書きすることで評価に影響を及ぼします。式x + (y + z)では、xを加える前にy + zの加算が行われます。

The Go Programming Language Specificationの翻訳、8回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


宣言とスコープ

宣言をおこなうことによって識別子(ブランク識別子を除く)は、定数、型、変数、関数、パッケージにバインドされます。プログラム内のすべての識別子は、宣言されなければなりません。識別子は同じブロック内で2回宣言されることはありません、また識別子をファイルブロックとパッケージブロックの両方で宣言することはできません。

Declaration   = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .

スコープとは、ソース内で宣言済み識別子に割り当てられた定数、型、変数、関数、パッケージを参照可能な範囲のことです。

Go言語ではブロックを使ってスコープ制御を行います。

  1. 事前宣言済み識別子が持つスコープは、ユニバースブロックです。
  2. トップレベル(関数の外)にて宣言されている定数、型、変数、関数の識別子が持つスコープは、パッケージブロックです。
  3. インポートされたパッケージの識別子が持つスコープは、そのimport宣言を含んでいるファイルブロックです。
  4. 関数のパラメータまたは戻り値として宣言されている変数の識別子が持つスコープは、関数の本体部分です。
  5. 関数のなかで宣言されている定数または変数の識別子が持つスコープは、ConstSpecまたはVarSpecの直後から始まり、その識別子を含んでいるブロックの終わりまでです。
  6. 関数内で宣言した型の識別子が持つスコープは、TypeSpecの識別子から始まり、その識別子を含んでいるブロックの終わりまでです。

ブロック内で宣言された識別子は、内側のブロック内で再宣言されます。ブロック内で宣言した識別子がスコープ内にある間、その識別子は宣言したときの実体を表しつづけます。

パッケージ節は宣言ではありません。パッケージ名がどのスコープにも現れることがないからです。パッケージ節は同一のパッケージに所属するファイルを識別するためと、インポートされるときにデフォルトとして使われるパッケージ名を定義するためのものです。

ラベルのスコープ

ラベルはラベルステートメントによって宣言され、breakcontinuegotoステートメント内で使用されます(§Break ステートメント、§Continue ステートメント、§Goto ステートメント)。ラベルはその他の識別子と異なり、ブロックによるスコープ制御は行われず、ラベル以外の識別子との重複が許されます。ラベルのスコープは、そのラベルが宣言されている関数の本体内ですが、ネストした関数はスコープ外です。

事前宣言済み識別子

下に示す識別子は、ユニバースブロック内で明示的に宣言されています。

基本型:
	bool byte float32 float64 int8 int16 int32 int64
	string uint8 uint16 uint32 uint64

アーキテクチャ仕様に従う型:
	float int uint uintptr

定数:
	true false iota

ゼロ値:
	nil

関数:
	cap close closed len make new panic panicln print println

エクスポートされた識別子

識別子は、他パッケージからの限定付き識別子を使ったアクセスを許可するためにエクポートされることがあります。識別子は下の2つの条件を満たすときエクスポートされます。

  1. 識別子の名前の最初の一文字が大文字(ユニコードクラス”Lu”)である。なおかつ
  2. 識別子がパッケージブロック内で宣言されている、またはパッケージブロック内で宣言された型のフィールドかメソッドである。

それ以外の識別子がエクスポートされることはありません。

ブランク識別子

アンダースコア(_)を使って記述されるブランク識別子は、他の識別子と同じく宣言にて使用されますが、その宣言でバインドは行われません。

定数の宣言

定数の宣言では、識別子(定数名)のカンマ区切りのリストを、定数式のリストにバインドします。識別子と式の数は一致している必要があり、左辺のn番目の識別子が、右辺のn番目の式にバインドされます。

ConstDecl      = "const" ( ConstSpec | "(" [ ConstSpecList ] ")" ) .
ConstSpecList  = ConstSpec { ";" ConstSpec } [ ";" ] .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .

IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

定数の宣言時に型を指定したときは、定数はその型になり、式はその型に対して代入の適合性を持たなくてはならなくなります。型を省略したときは、型と対応する式によってそれぞれの型が決まります。式の値が型を持たない定数のときは、宣言された定数の識別子も同様に型を持たない定数値を表します。たとえば式が浮動小数点リテラルのとき、たとえ小数部の値が0であっても、識別子は浮動小数点の定数を表します。

const Pi float64 = 3.14159265358979323846
const zero = 0.0             // 型を持たない浮動小数点定数
const (
	size int64 = 1024;
	eof = -1;            // 型を持たない整数定数
)
const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", 型を持たない整数と文字列定数
const u, v float = 0, 3      // u = 0.0, v = 3.0

括弧でくくられたconst宣言リスト内では、式リストは最初の宣言以外では省略可能です。このように式リストが省略されたときは、直近で使用した式リスト(型が指定されていれば型も)が適用されます。したがって、式リストを省略することは前の式リストを繰り返すことと同じになります。識別子の数は、直前の式リスト内の式の数と一致していなければなりません。iota定数ジェネレータを使うことで、シーケンシャルな値の宣言が簡単に行えるようになります。

const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Partyday;
	numberOfDays;  // この定数はエクスポートされない
)

Iota

定数の宣言で使用される事前宣言済み擬似定数iotaは、連続した型を持たない整数定数値を生成します。予約語constがソース内に現れると値が0にリセットされ、セミコロンが現れると値がひとつ増やされます。これは関連し合う定数グループを作成するときに利用されます。

const (  // iotaは0にリセットされる
	c0 = iota;  // c0 == 0
	c1 = iota;  // c1 == 1
	c2 = iota   // c2 == 2
)

const (
	a = 1 << iota;  // a == 1 (iotaはリセット済み)
	b = 1 << iota;  // b == 2
	c = 1 << iota;  // c == 4
)

const (
	u       = iota * 42;  // u == 0     (型を持たない整数定数)
	v float = iota * 42;  // v == 42.0  (浮動小数点定数)
	w       = iota * 42;  // w == 84    (型を持たない整数定数)
)

const x = iota;  // x == 0 (iotaはリセット済み)
const y = iota;  // y == 0 (iotaはリセット済み)

ひとつの式リスト内では、iotaの値は常に同じです。これはiotaの値はセミコロンが現れた時だけ増やされるためです。

const (
	bit0, mask0 = 1 << iota, 1 << iota - 1;  // bit0 == 1, mask0 == 0
	bit1, mask1;                             // bit1 == 2, mask1 == 1
	_, _;                                    // スキップ iota == 2
	bit3, mask3;                             // bit3 == 8, mask3 == 7
)

上の例は、直前の式リストを繰り返し適用し続けています。

型の宣言

型の宣言は型名となる識別子を、元になる型と同じ定義(エレメント、フィールド、チャネルの方向など)を持った新しい型としてバインドします。
また新しい型は、元になった型とは互換性がありますが異なる型です。

TypeDecl     = "type" ( TypeSpec | "(" [ TypeSpecList ] ")" ) .
TypeSpecList = TypeSpec { ";" TypeSpec } [ ";" ] .
TypeSpec     = identifier Type .
type IntArray [16]int

type (
	Point struct { x, y float };
	Polar Point
)

type TreeNode struct {
	left, right *TreeNode;
	value *Comparable;
}

type Cipher interface {
	BlockSize() int;
	Encrypt(src, dst []byte);
	Decrypt(src, dst []byte);
}

宣言された型は、バインドした型からはメソッドを継承しません。しかしコンポジット型の要素が持っているメソッド群はそのまま残ります。

// MutexはLockとUnlockメソッドを持つデータ型
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutexはMutexと同じ構成ではあるが、メソッド群は空である
type NewMutex Mutex

// PrintableMutexのメソッド群には、匿名フィールドMutexに
// バインドされているLockとUnlockメソッドが含まれる
type PrintableMutex struct {
	Mutex;
}

論理値型、数値型、文字列型から別の型を定義してメソッドを追加するために、型の宣言が使われることがあります。

type TimeZone int

const (
	EST TimeZone = -(5 + iota);
	CST;
	MST;
	PST;
)

func (tz TimeZone) String() string {
	return fmt.Sprintf("GMT+%dh", tz);
}

変数の宣言

変数は宣言によって作成、バインドされ、型と必要であれば初期値を与えられます。

VarDecl     = "var" ( VarSpec | "(" [ VarSpecList ] ")" ) .
VarSpecList = VarSpec { ";" VarSpec } [ ";" ] .
VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float
var k = 0
var x, y float = -1, -2
var (
	i int;
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name];  // マップからの引当。存在するかどうかだけ知りたい

式リストが与えられたときは、変数にはその式が順番に代入 (§代入)され、初期化されます。そのときは、式リスト内のすべての式が代入に使用され、全ての変数がそれによって初期化される必要があります。式リストが与えられなければ、各変数はゼロ値で初期化されます。

型が指定されたときは、変数はその型になります。指定されないときは、代入された式から型が推測されます。

型が指定されず、かつ対応する式が型を持たない定数のときは、宣言された変数の型は、boolintfloatstringのいずれかになります。どれになるかは定数の値に依存します。

var b = true    // tはbool型
var i = 0       // iはint型
var f = 3.0     // fはfloat型
var s = "OMDB"  // sはstring型

省略形式による変数の宣言

省略形式による変数の宣言には、下の文法が使われます。

ShortVarDecl = IdentifierList ":=" ExpressionList .

これは変数の宣言の省略形式で、初期化式は伴いますが型の指定は行いません。

"var" IdentifierList = ExpressionList .
i, j := 0, 10;
f := func() int { return 7; }
ch := make(chan int);
r, w := os.Pipe(fd);  // os.Pipe()は戻り値を2つ返す
_, y, _ := coord(p);  // coord()は戻り値を3つ返すが、y座標だけ使用する

非省略形式とは異なり、この省略形式による変数の宣言は、同じブロック内で、同じ型として宣言された変数に対して再宣言が可能です。ただし、このとき宣言する変数のうち少なくともひとつは、ブランク変数ではない新しい変数である必要があります。結果として、再宣言は複数の値に対してのみ適用できます。再宣言では新しい変数を作成するのではなく、単に変数に新しい値を割り当てます。

field1, offset := nextField(str, 0);
field2, offset := nextField(str, offset);  // offsetの再宣言

省略形式は関数内でのみ使用されます。また省略形式はifforswitchの初期化コンテキストにおいて、一時的なローカル変数を宣言するために用いられます(§ステートメント)。

関数の宣言

関数の宣言は、識別子と関数をバインドします(§関数型)。

FunctionDecl = "func" identifier Signature [ Body ] .
Body         = Block.

関数の宣言の際、関数の本体部分を省略することが可能です。このような宣言はアセンブリルーチンのようなGo言語の外で実装された関数へのシグネチャを定義します。

func min(x int, y int) int {
	if x < y {
		return x;
	}
	return y;
}

func flushICache(begin, end uintptr)  // 外部にて実装

メソッドの宣言

メソッドの宣言は、識別子とメソッド(レシーバを伴った関数)をバインドします。

MethodDecl   = "func" Receiver MethodName Signature [ Body ] .
Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
BaseTypeName = identifier .

Tが型の名前とすると、レシーバの型はTまたは*Tの形式でなければなりません。Tはレシーバベース型、または単にベース型と呼ばれます。このベース型はポインタまたはインタフェース型であってはならず、またメソッドと同じパッケージ内で宣言されていなくてはなりません。メソッドの宣言は、「メソッドをベース型にバインドする」とも言われます。メソッドはセレクタを使ってその型と一緒に指定したときのみ参照可能になります (§型の宣言、§セレクタ)。

Point型にメソッドの宣言を行います。

func (p *Point) Length() float {
	return Math.sqrt(p.x * p.x + p.y * p.y);
}

func (p *Point) Scale(factor float) {
	p.x = p.x * factor;
	p.y = p.y * factor;
}

レシーバ型が*PointLengthメソッドとScaleメソッドを、ベース型Pointにバインドしています。

レシーバの値がメソッドの本体部で参照されないのであれば、宣言時にレシーバの識別子を省略可能です。同じことは、一般に関数とメソッドのパラメータにもあてはまります。

メソッドの型は、第一パラメータにレシーバを伴った関数の型となります。例えばScaleメソッドは次の型になります。

(p *Point, factor float)

しかし、このように宣言しても関数はメソッドにはなりません。

The Go Programming Language Specificationの翻訳、7回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


ブロック

ブロックは、対になる波括弧{}内の一連の宣言とステートメントです。

Block = "{" StatementList "}" .

ソースコード内には明示的なブロックの他に、下に示す潜在的なブロックがあります。

  1. すべてのGo言語ソースを包括する、ユニバースブロック。
  2. パッケージ内の全Go言語ソースを包括する、パッケージブロック。
  3. 各ファイル内の全Go言語ソースを包括する、ファイルブロック。
  4. ifforswitchステートメントは、それ自身が存在的なブロックであるとみなされます。
  5. switchselectステートメント内の各条項は、存在的なブロックの働きをします。

ブロックはネストし、スコープに影響します。

The Go Programming Language Specificationの翻訳、6回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


型と値の特性

2つの型があるとします。これらは同じ型であったり、異なる型であったり、互換性を持つ型どうしだったり、互換性を持たなかったりします。同じ型どうしは常に互換性を持っていますが、2つの型が互換性を持つからといって同一の型であるとは限りません。

型の同一性と互換性

型の同一性

2つの型の名前が同一の宣言によるものなら、これら2つの型は同一です(§宣言とスコープ)。名前を持つ型と、無名の型が同一の型であることはあり得ません。2つの無名の型が、対応する型リテラルが同じリテラル構造を持ち、かつ対応する構成要素が同一の型であれば、この2つは同一の型です。詳しく説明します。

  • 配列型の場合は、要素の型と配列の長さが同じであれば同一の型です。
  • スライス型の場合は、要素の型が同じであれば同一の型です。
  • 構造体型の場合は、フィールドの並び順と、対応するフィールドの名前と型が同じであれば同一の型です。このとき匿名フィールドどうしは同じ名前であるとみなされます。
  • ポインタ型の場合は、ベース型が同じであれば同一の型です。
  • 関数型の場合は、パラメータと戻り値の個数、および対応するパラメータと戻り値の型がそれぞれ同じであれば同一の型です。“…”パラメータはすべて同一の型として規定されています。パラメータと戻り値の名前は比較には使われません。
  • インタフェース型の場合は、同じ名前、同じ関数型のメソッド群を持っていれば同一の型です。メソッドの順序は関係ありません。
  • マップ型の場合は、キーと値の型が同じであれば同一の型です。
  • チャネル型の場合は、値の型と方向が同じであれば同一の型です。

型の互換性

型の互換性は、同一性ほどは厳密ではありません。型の名前の有り無しに関わらず、お互いの型リテラルが互換性を持ってさえいれば、2つの型には互換性があります。他の点において、型の互換性の定義は先ほど解説した型の同一性と同じなので、「同一」を「互換性を持つ」に置き換えてみてください。

宣言をおこないます。

type (
	T0 []string;
	T1 []string;
	T2 struct { a, b int };
	T3 struct { a, c int };
	T4 func (int, float) *T0;
	T5 func (x int, y float) *[]string;
)

次の型どうしは同一です。

T0 と T0
[]int と []int
struct { a, b *T5 } と struct { a, b *T5 }
func (x int, y float) *[]string と func (int, float) (result *[]string)

T0T1は同一でもなければ、互換性もありません。それは、これらが別個に宣言された名前付きの型であるためです。

次の型どうしは互換性があります。

T0 と T0
T0 と []string
T3 と struct { a int; c int }
T4 と func (x int, y float) *[]string

T2struct { a, c int }には互換性はありません。それはフィールド名が異なるためです。

代入の適合性

下の条件をひとつ以上満たすとき、静的な型Vの値であるvは、型Tに代入できます。

  • VTに互換性がある。
  • Tはインタフェース型であり、VT実装している。
  • Vは配列のポインタで、Tはそれと互換性のある要素型を持ったスライスであり、なおかつVTどちらかが名前を持たない。(代入後、スライスの変数は配列を参照するようになります。要素はコピーされるわけではありません。)
  • Vは双方向チャネルで、Tはそれと互換性のある要素型を持ったチャネルであり、すくなくともVTどちらかが名前を持たない。

Tが構造体のときは、Tの全フィールドがエクスポートされているか、またはTが宣言されているパッケージ内で代入が行われる必要があります。言い換えると構造体型の値を構造体型の変数に代入できるのは、構造体の全フィールドが個別に代入可能なときだけです。

型を持たない定数vが、型Tの値を正しく表すことができるならば、vは型Tに対し代入の適合性を持ちます。

事前宣言済み識別子nilは、ポインタ・関数・スライス・マップ・チャネル・インタフェース型いずれにも代入の適合性を持ち、それらの型のゼロ値を表します。

また、ブランク識別子にはどんな値でも代入可能です。

比較の適合性

あるケースを除き、すべての型は、互換性がある他の静的な型と値を比較することができます。数値型と文字列型の値は、全種類の比較演算子を使って比較することができますが、論理値型はイコールかそうでないかの比較のみできます。

コンポジット型の値は演算子==!=を使用して、イコールかそうでないか比較します。以下に但し書きがあります。

  • 配列および構造体は、どれとも比較できません。
  • スライスの値はnilとだけ比較できます。スライスの値がnilとなるのは、明示的にnilを代入したか、初期化されていないか、nil値を持つ他のスライスの値を代入したときだけです。
  • インタフェースの値がnilとイコールになるのは、nilを明示的に代入したか、初期化されていないか、nil値を持つ他のインタフェースの値を代入したときです。
  • nilと比較可能な型どうしは、2つの値がともにnilであればイコールであり、一方だけがnilであればイコールではありません。
  • ポインタの値は、双方が同じ位置を指し示していればイコールです。
  • 関数の値は、双方が同じ関数を参照していればイコールです。
  • チャネルとマップの値は、双方が同一のmakeの呼び出して作成されたときにイコールとなります(§スライス、マップ、チャネルの作成)。2つのチャネル型の値を比較する際、チャネルの型どうしが互換性を持っている必要がありますが、チャネルの方向は無視されます。
  • インタフェースの値は、双方の静的な型が互換性を持っていれば比較できます。イコールとなるのは双方が同じ動的な型をもち、かつその値自体がイコールのときだけです。