コンテンツにスキップ

Go/式

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go


[編集]

式(An expression)は、演算子や関数をオペランドに適用して値を計算することを指定します[1]

オペランド

[編集]

オペランド(Operands)とは、式の中の基本的な値を表すものです[2]。オペランドは、リテラル、定数、変数、関数を表す(修飾されている場合もある)ブランクでない識別子、または括弧で囲まれた式です。

構文
Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" ;
Literal     = BasicLit | CompositeLit | FunctionLit ;
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit ;
OperandName = identifier | QualifiedIdent ;
Go1.18からオペランドも型仮引数を持つことが出来るようになりました。

ジェネリック関数を示すオペランド名の後に、型引数のリストを続けることができます;結果として生じるオペランドは、インスタンス化された関数です。 空白の識別子(_)は,代入の左側においてのみオペランドとして現れることができます。

実装上の制限
オペランドの型が空の型パラメータである場合、コンパイラはエラーを報告する必要はありません。
このような型パラメータを持つ関数はインスタンス化できません。
インスタンス化しようとすると、その場所でエラーになります。

修飾された識別子

[編集]

修飾された識別子(Qualified identifiers)とは、パッケージ名のプレフィックスで修飾された識別子のことです[3]。パッケージ名と識別子の両方が空白であってはなりません。

構文
QualifiedIdent = PackageName "." identifier ;

修飾された識別子は、別のパッケージにある識別子にアクセスするもので、インポートされなければなりません。識別子は、そのパッケージのパッケージブロックにエクスポートして宣言する必要があります。

math.Sin // パッケージmathのSin関数を示す。

複合リテラル

[編集]

複合リテラル(Composite literals)は、評価されるたびに新しい複合値を構築します[4]。複合リテラルは、リテラルの型に続いて、要素の中括弧付きリストで構成されます。各要素の前には、対応するキーを付けることができます。

構文
CompositeLit  = LiteralType LiteralValue ;
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType | SliceType | MapType | TypeName ;
LiteralValue  = "{" [ ElementList [ "," ] ] "}" ;
ElementList   = KeyedElement { "," KeyedElement } ;
KeyedElement  = [ Key ":" ] Element ;
Key           = FieldName | Expression | LiteralValue ;
FieldName     = identifier ;
Element       = Expression | LiteralValue ;

LiteralType のコア型 T は、構造体、配列、スライス、またはマップ型でなければなりません (文法では、型が TypeName として指定される場合を除き、この制約が適用されます)。 要素とキーの型は、型Tのそれぞれのフィールド、要素、キー型に割り当て可能でなければなりません。 キーは、構造体リテラルではフィールド名として、配列およびスライスリテラルではインデックスとして、マップリテラルではキーとして解釈されます。 マップリテラルの場合、すべての要素にキーが必要です。 同じフィールド名や定数キー値で複数の要素を指定するとエラーになります。

関数リテラル

[編集]

関数リテラル(A function literal )は、匿名関数を表します[5]

構文
FunctionLit = "func" Signature FunctionBody ;
func(a, b int, z float64) bool { return a*b < int(z) }

関数リテラルは、変数に代入したり、直接呼び出すことができます。

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

関数リテラルはクロージャであり、周囲の関数で定義された変数を参照することができます。これらの変数は、周辺の関数と関数リテラルの間で共有され、アクセス可能である限り存続します。

package main

import "fmt"

func Map(f func(int) int, array []int) []int {
	result := make([]int, len(array))
	for i, v := range array {
		result[i] = f(v)
	}
	return result
}

func main() {
	fmt.Println(Map(func(x int) int { return 2*x + 1 }, []int{1, 2, 3}))
}

一次式

[編集]

一次式(Primary expressions)は、単項式や二項式のオペランドになります[6]

構文
PrimaryExpr =
	Operand |
	Conversion |
	MethodExpr |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Arguments ;

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

セレクター

[編集]

セレクター(Selectors)[7]

次の x がパッケージ名でない一次式であれば、セレクター式です。

x.f

は、値xのフィールドまたはメソッドfを表します(*xの場合もあります。 識別子fは、(フィールドまたはメソッド)セレクターと呼ばれ、ブランク識別子であってはなりません。 xがパッケージ名の場合は、修飾された識別子のセクションを参照してください。

セレクターfは、型Tのフィールドやメソッドfを示すこともあれば、Tの入れ子になった埋め込みフィールドのフィールドやメソッドfを参照することもあります。 fに到達するまでに走査された埋め込みフィールドの数を、Tにおける深さ(The depth)と呼びます。 Tの埋め込みフィールドAで宣言されたフィールドまたはメソッドfの深さは、Aのfの深さに1を加えたものです。

セレクタには以下のルールが適用されます。

  1. Tがポインターやインタフェース型ではないTまたは*T型の値xに対しては、x.fはTの中で最も浅い深さにあるフィールドまたはメソッドを示し、そのようなfが存在します。
  2. Iがインタフェース型であるI型の値xに対して、x.fはxの動的な値の名前fを持つ実際のメソッドを示す。Iのメソッドセットに名前fを持つメソッドがない場合、セレクター式は違法である。
  3. 例外として、xの型が定義されたポインター型で、(*x).fがフィールド(メソッドではない)を表す有効なセレクター式の場合、x.fは(*x).fの短縮形となります。それ以外の場合には、x.fは不正です。
  4. xがポインター型で値がnilであり、x.fが構造体のフィールドを示す場合、x.fへの代入や評価はランタイム・パニックを引き起こします。
  5. xがインターフェイス型で値がnilの場合、メソッドx.fを呼び出したり評価したりすると、ランタイム・パニックが発生します。
宣言の例
type T0 struct {
	x int
}

func (*T0) M0()

type T1 struct {
	y int
}

func (T1) M1()

type T2 struct {
	z int
	T1
	*T0
}

func (*T2) M2()

type Q *T2

var t T2     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil 
var q Q = p
次のように書けます
t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

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

q.x          // (*(*q).T0).x        (*q).x is a valid field selector

p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
p.M2()       // p.M2()              M2 expects *T2 receiver
t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls
以下は無効です
q.M0()       // (*q).M0 is valid but not a field selector

メソッド式

[編集]

メソッド式(Method expressions)[8]

Mが型Tのメソッドの集合にある場合、T.Mは、Mと同じ引数にメソッドのレシーバーである追加の引数を前置した通常の関数として呼び出し可能な関数である。

構文
MethodExpr    = ReceiverType "." MethodName ; 
ReceiverType  = Type ;

T型の構造体に、T型のレシーバーを持つMvと、*T型のレシーバーを持つMpという2つのメソッドがあるとします。

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver
 
var t T
T.Mv

は、Mvと同等の関数を生成しますが、第1引数に明示的なレシーバーを指定します。これは、

シグネチャ
func(tv T, a int) int

この関数は通常、明示的なレシーバーを使って呼び出すことができるので、これらの5つの呼び出しは同等です。

さまざまな呼び出し
t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7) 
f2 := (T).Mv; f2(t, 7)

同様に、

(*T).Mp

のシグネチャーを持つMpを表す関数値が得られます。


func(tp *T, f float32) float32

値のレシーバーを持つメソッドでは,明示的なポインターのレシーバーを持つ関数を派生させることができます.

(*T).Mv

というシグネチャを持つMvを表す関数値が得られます。

func(tv *T, a int) int

このような関数は、レシーバーを介して間接的に値を生成し、基礎となるメソッドにレシーバーとして渡します。このメソッドは、関数呼び出しでアドレスが渡された値を上書きしません。

ポインター・レシーバー・メソッドに対する値レシーバー関数という最後のケースは、ポインター・レシーバー・メソッドが値型のメソッド・セットに含まれていないため、違法です。

メソッドから派生した関数値は、関数呼び出し構文で呼び出され、レシーバーは呼び出しの第一引数として提供されます。つまり、f := T.Mvが与えられた場合、fはt.f(7)ではなくf(t, 7)として呼び出されます。レシーバーを束縛する関数を構築するには、関数リテラルまたはメソッド値を使用します。

インターフェイス型のメソッドから関数値を派生させることは合法です。結果として得られる関数は、そのインタフェース型の明示的なレシーバーを取ります。

メソッドの値

[編集]

メソッドの値(Method values)[9]

式xが静的型Tを持ち、Mが型Tのメソッドセットにある場合、x.Mはメソッド値と呼ばれる。メソッド値x.Mは、x.Mのメソッド呼び出しと同じ引数で呼び出し可能な関数値である。式xはメソッド値の評価中に評価されて保存され、保存されたコピーは後に実行される可能性のある呼び出しのレシーバーとして使用される。

型Tはインターフェイス型でも非インターフェイス型でもよい。

前述のメソッド式の説明と同様に、型Tの構造体に2つのメソッドMv(レシーバーがT型)とMp(レシーバーが*T型)があるとします。

type T struct {
	a int
}

func (tv T) Mv(a int) int          { return 0 } // 値のレシーバー
func (tp *T) Mp(f float32) float32 { return 1 } // ポインターレシーバー

var t T
var pt *T

func makeT() T
t.Mv
は、次の型の関数値を得ることができます。
func(int) int

次の2つの呼び出しは等価です。

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

同様に、

pt.Mp
は、次の型の関数値が得られます。
func(float32) float32

セレクタと同様に、ポインタを使った値のレシーバを持つ非インターフェイスのメソッドを参照すると、そのポインタは自動的に参照解除されます:pt.Mvは(*pt).Mvと同等です。

メソッドコールと同様に、アドレス指定可能な値を使用したポインターレシーバを持つ非インターフェースメソッドへの参照は、自動的にその値のアドレスを取ります:t.Mpは(&t).Mpと同等です。

f := t.Mv; f(7)  // t.Mv(7)と同じです。
f := pt.Mp; f(7) // pt.Mp(7)のようになります。
f := pt.Mv; f(7) // (*pt).Mv(7)のようになります。
f := t.Mp; f(7)  // (&t).Mp(7)のようになります。
f := makeT().Mp  // 無効:makeT()の結果はアドレス指定できない

上記の例では非インターフェイス型を使用していますが、インターフェイス型の値からメソッドの値を生成することも合法です。

var i interface { M(int) } = myVal
f := i.M; f(7) // i.M(7)のようなもの

インデックス式

[編集]

インデックス式(Index expressions)[10]

一次式
a[x]

は、配列の要素、配列へのポインタ、スライス、文字列、マップのうち、x でインデックスされたものを表し、以下のルールが適用されます。

a がマップでない場合
インデックス x は、整数型または型付けされていない定数でなければなりません。
定数のインデックスは非負であり、int型の値で表現できなければならない。
型付けされていない定数のインデックスはint型で与えられる。
インデックス x は、0 <= x < len(a) の場合は in range であり、そうでない場合は out of range である。
aが配列型 A の場合
aの定数インデックスは範囲内でなければならない
実行時に x が範囲外の場合、ランタイム・パニックが発生します。
a[x] はインデックス x の配列要素であり、a[x] の型は A の要素の型である。
aが配列型へのポインタ A の場合
a[x](*a)[x] の省略形です。
aがスライス型 S の場合
実行時にxが範囲外の場合、ランタイム・パニックが発生する
a[x] はインデックス x のスライス要素であり、a[x] の型は S の要素型です。
aが文字列型の場合
文字列aが定数の場合、定数のインデックスは範囲内でなければなりません。
実行時に x が範囲外の場合、ランタイム・パニックが発生します。
a[x] はインデックス x の非定数のバイト値であり、a[x] の型はbyteである。
a[x] に代入することはできない。
aがマップ型 M の場合
xの型は、M のキー型に割り当て可能でなければならない。
マップにキーxのエントリがある場合、a[x] はキーxのマップ要素であり、a[x] のタイプは M の要素タイプである。
マップが nil であるか、そのようなエントリを含まない場合、a[x]M の要素タイプのゼロ値である.
それ以外の場合、a[x] は不正である。

map[K]V型のマップaに対するインデックス式で、特別な形式の代入または初期化で使用されます。

v, ok = a[x] (v, ok = a[x])
v, ok := a[x] (v, ok = a[x])
var v, ok = a[x].

は、追加の型付けされていない真理値をもたらします。ok の値は、キー x がマップ内に存在する場合は true、そうでない場合は false です。

nilマップの要素に代入するとランタイムパニックを起こします。

スライス式

[編集]

スライス式(Slice expressions)は、文字列、配列、配列へのポインタ、スライスから部分文字列やスライスを構成します[11]。下限と上限を指定する単純な形式と、容量の上限も指定する完全な形式の2つのバリエーションがあります。

単純なスライス式

[編集]

単純なスライス式(Simple slice expressions)

文字列、配列、配列へのポインタ、またはスライス a に対して、

一次式
a[low : high]

は、部分文字列またはスライスを構成します。添字 low と high は、オペランド a のどの要素が結果に現れるかを選択します。結果は0から始まるインデックスを持ち、長さはhigh - lowになります。配列aをスライスした後

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

このスライスsは、型が[]int、長さが3、容量が4、そして要素が

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

便宜上、いずれかのインデックスを省略することができます.低位のインデックスがない場合のデフォルトはゼロで、高位のインデックスがない場合のデフォルトはスライスされたオペランドの長さとなります。

a[2:] // a[2:len(a)] と同じです。
a[:3] // a[0:3] と同じです。
a[:] // a[0:len(a)]と同じです。

aが配列へのポインタの場合、a[low : high]は(*a)[low : high]の省略形です.

配列や文字列の場合、0 <= low <= high <= len(a) であればインデックスは範囲内にあり、そうでなければ範囲外となります.スライスの場合、インデックスの上限は、長さではなく、スライスの容量cap(a)です。定数インデックスは、非負であり、int型の値で表現できなければならない。配列や定数文字列の場合、定数インデックスも範囲内でなければならない。配列や定数文字列の場合、定数インデックスも範囲内でなければなりません。また、両方のインデックスが定数の場合、low <= highを満たさなければなりません。実行時にインデックスが範囲外の場合、ランタイムパニックが発生します。

型付けされていない文字列を除き、スライスされたオペランドが文字列またはスライスの場合、スライス操作の結果はオペランドと同じ型の非定数値になります。型付けされていない文字列オペランドの場合、結果はstring型の非定数値となります。スライスされたオペランドが配列の場合、それはアドレス可能でなければならず、スライス操作の結果は、配列と同じ要素タイプのスライスとなります。

有効なスライス式のスライスされたオペランドが nil スライスの場合、結果は nil スライスになります。それ以外の場合、結果がスライスであれば、オペランドと基礎となる配列を共有します。

var a [10]int
s1 := a[3:7] // s1の基底配列は配列a; &s1[2] == &a[5] です。
s2 := s1[1:4] // s2の下の配列は、s1の下の配列であり、配列aである。
s2[1] = 42 // s2[1] == s1[2] == a[5] == 42; これらはすべて、同じ基礎となる配列要素を参照しています。

完全なスライス式

[編集]

完全なスライス式(Full slice expressions)

配列、配列へのポインタ、スライスa(文字列は不可)の場合、一次式は

a[low : high : max]

は、単純なスライス式a[low : high]と同じ長さと要素を持つ、同じ型のスライスを構築します。さらに、結果として得られるスライスの容量を max - low に設定して制御します。最初のインデックスだけは省略可能で、デフォルトでは0です。

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

スライスtの型は[]int、長さ2、容量4、要素は

t[0] == 2
t[1] == 3

単純なスライス式としては、aが配列へのポインタの場合、a[low : high : max]は(*a)[low : high : max]の省略形となります。スライスされたオペランドが配列の場合は、アドレス指定可能でなければなりません。

添字は、0 <= low <= high <= max <= cap(a) であれば範囲内、そうでなければ範囲外となります。定数のインデックスは、非負で、int型の値で表現できなければなりません。複数のインデックスが定数の場合、存在する定数はお互いに範囲内でなければなりません。実行時にインデックスが範囲外である場合、ランタイム・パニックが発生します。

型アサーション

[編集]

型アサーション(Type assertions)[12]

インターフェイス型の式xと型Tに対して、一次式の

x.(T)

は、xがnilではないことと、xに格納されている値がT型であることを主張します。x.(T)という表記はタイプアサーションと呼ばれる。

より正確には、Tがインターフェース型でない場合、x.(T)はxの動的型がT型と同一であることを主張する。 この場合、Tはxの(インターフェース)型を実装しなければならない。 そうでなければ、xがT型の値を格納することはできないので、型の主張は無効である。 Tがインターフェイス型の場合、xの動的型がインターフェイスTを実装していることをx.(T)が主張する。

型の主張が成り立つ場合、式の値はxに格納されている値であり、その型はTである。 型の主張が偽の場合、ランタイムパニックが発生する。 つまり、xの動的な型は実行時にしかわからないにもかかわらず、正しいプログラムではx.(T)の型はTであることがわかっているのです。

var x interface{} = 7 // x は動的型が int で値が 7 です。
i := x.(int) // iはint型で値は7です。

type I interface { m() }.

func f(y I) {.
	s := y.(string) // 不正: stringはIを実装していない (メソッドmがない)
	r := y.(io.Reader) // r は io.Reader 型であり、y の動的型は I と io.Reader の両方を実装していなければならない
	...
}

特殊な形式の代入または初期化で使用されるタイプアサーション

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // v と ok の動的な型は T と bool です。

は、追加の型付けされていないブール値を出力します。 アサーションが成立していれば ok の値は真です。 それ以外の場合は偽となり、vの値はT型のゼロ値となります。 この場合、ランタイム・パニックは発生しません。

呼び出し

[編集]

呼び出し(Calls)[13]

関数型Fの式fが与えられます。

f(a1, a2, ... an)

は、引数 a1, a2, ... an で f を呼び出します。1つの特別な場合を除いて、引数はFのパラメータ型に割り当て可能な単一値の式でなければならず、関数が呼び出される前に評価されます。 式の型はFの結果の型です。メソッドの呼び出しも同様ですが、メソッド自体は、メソッドのレシーバ型の値のセレクタとして指定されます。

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

関数呼び出しでは、関数値と引数が通常の順序で評価されます。 評価された後、呼び出しのパラメータは値で関数に渡され、呼び出された関数は実行を開始します。 関数の戻りパラメータは、関数が戻るときに呼び出し元に値で返されます。

nilの関数値を呼び出すと、ランタイム・パニックが発生します。

特殊なケースとして、関数やメソッドgの戻り値が同数で、別の関数やメソッドfのパラメータに個別に割り当て可能な場合、f(g(parameters_of_g))という呼び出しは、gの戻り値をfのパラメータに順に束縛した後にfを呼び出すことになります。 fの呼び出しには、gの呼び出し以外のパラメータを含んではならず、gは少なくとも1つの戻り値を持っていなければなりません。 fに最後の...パラメータがある場合は、通常のパラメータを割り当てた後に残ったgの戻り値が割り当てられます。

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

func Join(s, t string) 文字列 {.
	return s + t
}

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

メソッドコール x.m() は、x の (その型の) メソッドセットに m が含まれ、引数リストが m のパラメータリストに割り当てられる場合に有効です。 x がアドレス可能で、&x のメソッドセットに m が含まれる場合、x.m() は (&x).m() の短縮形となります。

var p ポイント
p.Scale(3.5)

明確なメソッドタイプはなく、メソッド・リテラルも存在しません。

仮引数に ... パラメーターを渡す

[編集]

仮引数に ... パラメーターを渡す(Passing arguments to ... parameters)[14]

fが、最終パラメータpが...T型のバリアディック型である場合、f内ではpの型は[]T型と同等です。 fがpの実際の引数を持たずに呼び出された場合、pに渡される値はnilです。 そうでなければ、渡される値は[]T型の新しいスライスで、連続した要素が実際の引数である新しい基礎的な配列であり、これらは全てTに割り当て可能でなければなりません。 したがって、スライスの長さと容量はpに束縛された引数の数であり、各呼び出しサイトで異なる可能性があります。

次のような関数と呼び出しがあります。

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

Greetingの中で、whoは最初の呼び出しではnilという値を持ち、2回目の呼び出しでは[]string{"Joe", "Anna", "Eileen"}という値を持ちます。

最後の引数がスライスタイプ []T に割り当て可能で、その後に ... が続く場合、それは ...T パラメータの値として変更されずに渡されます。この場合、新しいスライスは作成されません。

スライスsとコールが与えられると

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

を呼び出すと、Greetingの中では、同じ配列を持つsと同じ値を持つことになります。

演算子

[編集]

演算子(Operators)は、は、オペランドを組み合わせて式を作ります[15]

構文
Expression = UnaryExpr | Expression binary_op Expression ;
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr ;

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

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

比較については別の場所で説明します。他の二項演算子では、シフトや型付けされていない定数を含む演算でない限り、オペランドの型は同一でなければなりません。定数のみを扱う演算については、定数式の項を参照してください。

シフト演算を除き、一方のオペランドが型付けされていない定数で、もう一方のオペランドが型付けされていない場合、定数は暗黙のうちにもう一方のオペランドの型に変換されます。

シフト式の右オペランドは、整数型であるか、またはuint型の値で表現可能な型付けされていない定数でなければなりません。定数ではないシフト式の左オペランドが型付けされていない定数の場合、まずその左オペランドだけでシフト式を置き換えた場合に想定される型に暗黙のうちに変換されます。

var a [1024]byte
var s uint = 33

// The results of the following examples are given for 64-bit ints.
var i = 1<<s                   // 1 has type int
var j int32 = 1<<s             // 1 has type int32; j == 0
var k = uint64(1<<s)           // 1 has type uint64; k == 1<<33
var m int = 1.0<<s             // 1.0 has type int; m == 1<<33
var n = 1.0<<s == j            // 1.0 has type int32; n == true
var o = 1<<s == 2<<s           // 1 and 2 have type int; o == false
var p = 1<<s == 1<<33          // 1 has type int; p == true
var u = 1.0<<s                 // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0           // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0           // illegal: 1 has type float64, cannot shift
var v float32 = 1<<s           // illegal: 1 has type float32, cannot shift
var w int64 = 1.0<<33          // 1.0<<33 is a constant shift expression; w == 1<<33
var x = a[1.0<<s]              // panics: 1.0 has type int, but 1<<33 overflows array bounds
var b = make([]byte, 1.0<<s)   // 1.0 has type int; len(b) == 1<<33

// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0<<s            // 1.0 has type int; mm == 0
var oo = 1<<s == 2<<s          // 1 and 2 have type int; oo == true
var pp = 1<<s == 1<<33         // illegal: 1 has type int, but 1<<33 overflows int
var xx = a[1.0<<s]             // 1.0 has type int; xx == a[0] 
var bb = make([]byte, 1.0<<s)  // 1.0 has type int; len(bb) == 0

演算子の優先順位

[編集]

演算子の優先順位(Operator precedence)

単項演算子(UnaryExpr)が最も高い優先順位を持ちます。 ++および--演算子は、式ではなく文を形成するため、演算子の階層から外れます。 そのため、文 *p++ は (*p)++ と同じになります。

二項演算子には5つの優先順位があります。 乗算演算子の優先順位が最も高く、次に加算演算子、比較演算子、&&(論理AND)、最後に||(論理OR)が続きます。

二項演算子には5つの優先順位
優先順位 演算子
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||

同じ優先順位の二項演算子は、左から右へと関連付けられます。例えば、x / y * z は、(x / y) * z と同じです。

代入は演算子ではなく文なので、結合法則とは無関係です(Goでは、C言語のような多重代入 a = b = 0 は許されません)。
+x
23 + 3*x[i]
x <= f()
^a >> b
f() || g() 
x == y+1 && <-chanInt > 0

算術演算子

[編集]

算術演算子(Arithmetic operators)は、数値に適用され、第一オペランドと同じ型の結果を得ることができます。4つの標準的な算術演算子(+, -, *, /) は、整数型、浮動小数点型、複素数型に適用され、+は文字列にも適用されます。ビットごとの論理演算子とシフト演算子は、整数型にのみ適用されます[16]

算術演算子
記号 意味 取りうるオペランド
+ 和(連結 文字列) 整数型、浮動小数点数型、複素数値、文字列型
- 整数型、浮動小数点数型、複素数値、文字列型
* 整数型、浮動小数点数型、複素数値
/ 整数型、浮動小数点数型、複素数値
% 剰余 整数型
& ビット毎の論理積 整数型
| ビット毎の論理和 整数型
^ ビット毎の排他的論理和 整数型
&^ ビットクリア (AND NOT) 整数型
<< 左シフト 整数 << 整数 >= 0
>> 右シフト 整数 >> 整数 >= 0

整数演算子

[編集]

整数演算子(Integer operators)[16]

2つの整数値xとyに対して、整数の商 と、剰余 は以下の関係を満たす。

x / yを0に向かっての切捨て

除算の切捨て
x y x / y x % y
5 3 1 2
-5 3 -1 -2
5 -3 -1 2
-5 -3 1 -2

唯一の例外は、被除数xがint型のxに対して最も負の値である場合、2の補数の整数オーバーフローにより、商q = x / -1はxに等しい(およびr = 0)ということです。

x, q
int8 -128
int16 -32768
int32 -2147483648
int64 -9223372036854775808

除数が定数の場合は、ゼロであってはなりません。実行時に除数がゼロの場合、ランタイムパニックが発生します。被除数が非負で除数が定数の2乗の場合、除算は右シフトで置き換えられ、余剰の計算はビットごとのAND演算で置き換えられます。

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

シフト演算子は、左オペランドを右オペランドで指定されたシフトカウントだけシフトします。実行時にシフトカウントが負の値であった場合、ランタイム・パニックが発生します。シフト演算子は、左オペランドが符号付き整数の場合は算術シフトを、符号なし整数の場合は論理シフトを実行します。シフト回数には上限がありません。シフトは、シフトカウントnに対して、左オペランドが1ずつn回シフトされたように動作します。その結果、x << 1 は x*2 と同じで、x >> 1 は x/2 と同じですが、負の無限大に向かって切り捨てられます。

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

+x 0 + x に等しい
-x 符号反転 0 - x に等しい
^x ビットごとの補数 符号なしのxの場合はm = "すべてのビットを1にする "となり、符号付きxではm = -1
整数のオーバーフロー
[編集]

整数のオーバーフロー(Integer overflow)[16]

符号なし整数値の場合、+, -, *, <<の演算は剰余演算2n(nは符号なし整数の型のビット幅)で計算されます。 大雑把に言うと、これらの符号なし整数の演算はオーバーフロー時に上位ビットを捨てるので、プログラムでは「ラップアラウンド」に頼ることになります。

符号付き整数の場合、 +, -, *, /, <<の各演算は合法的にオーバーフローしても構いませんが、結果として得られる値は符号付き整数表現、演算、およびそのオペランドによって決定論的に定義され、存在します。 オーバーフローはランタイム パニックを引き起こしません。 コンパイラは、オーバーフローが発生しないことを前提にコードを最適化することはできません。 例えば、x < x + 1 が常に真であると仮定することはできません。

浮動小数点演算子

[編集]

浮動小数点演算子(Floating-point operators)[16]

浮動小数点および複素数の場合、+xはxと同じで、-xはxの(算術)否定です。浮動小数点または複素数のゼロ除算の結果は、IEEE-754規格以上には規定されておらず、ランタイム・パニックが発生するかどうかは実装に依存します。

実装では、複数の浮動小数点演算を1つの融合した演算にまとめ、場合によっては文をまたいで実行し、丸めて得られる値とは異なる結果を生成することがあります。明示的な浮動小数点型の変換では、対象となる型の精度に合わせて丸めるので、その丸め方を捨ててしまうような融合を防ぐことができます。

たとえば、一部のアーキテクチャでは、中間結果 x*y を丸めずに x*y + z を計算する「融合積和演算」("fused multiply and add"; FMA)命令が提供されています。これらの例では、Goの実装がその命令を使用できる場合を示しています。

// x*yが明示的に丸められていないので、rを計算するためにFMAが許可されています。
r  = x*y + z
r  = z;   r += x*y
t  = x*y; r = t + z
*p = x*y; r = *p + z
r  = x*y + float64(z)

// FMAは、x*yの丸めを省略することになるので、rの計算には使えません。
r  = float64(x*y) + z
r  = z; r += float64(x*y) 
t  = float64(x*y); r = t + z

文字列の連結

[編集]

文字列の連結(String concatenation)[16]

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

s := "こんにちは" + string(c)
s += " そして、さようなら "

文字列の加算では、オペランドを連結して新しい文字列を作成します。

比較演算子

[編集]

比較演算子(Comparison operators)は、2つのオペランドを比較し、型付けされていないブール値を出力します[17]

比較演算子
== 一致
!= 不一致
< より小
<= より小あるいは一致
> より大
>= より大あるいは一致

どのような比較においても、第1オペランドは第2オペランドの型に代入可能でなければならず、またその逆も同様です。

等式演算子 == および != は、比較可能なオペランドに適用されます。順序付け演算子 <, <=, >, >= は、オペランドが順序付けられている場合に適用されます。これらの用語と比較の結果は次のように定義されます。

  • ブール値は比較可能です。2つのブール値は、それらが両方ともtrueまたは両方ともfalseである場合、等しい。
  • 整数値は、通常の方法で比較可能であり、順序付けられています。
  • 浮動小数点値は、IEEE-754規格で定義されているように、比較可能で順序付けられています。
  • 複素数の値は比較可能です。uvの2つの複素数値は、 real(u) == real(v)imag(u) == imag(v)の両方であれば等しい。
  • ポインター値は比較可能です。2つのポインター値は、それらが同じ変数を指しているか、または両方が値 nil を持っている場合、等しい。異なるゼロサイズの変数へのポインターは、等しいかもしれないし、そうでないかもしれない。
  • チャネル値は比較可能です。2つのチャンネル値は、makeの同じ呼び出しによって作成された場合、または両方とも値がnilである場合には等しい。
  • インターフェース値は比較可能です。2つのインタフェース値は、それらが同一のダイナミック・タイプと同一のダイナミック・バリューを持っているか、または両方が値nilを持っている場合、等しい。
  • 非インタフェース型の X の値 x とインタフェース型の T の値 t は、 X 型の値が比較可能で、 XT を実装している場合には、比較可能である。tの動的な型がXと同一であり、 tの動的な値がxと等しい場合、それらは同等である。
  • 構造体の値は、それらのすべてのフィールドが同等であれば、同等である。2つの構造体の値は、それらの対応する非空白のフィールドが等しい場合、等しいです。
  • 配列要素タイプの値が比較可能な場合、配列値は比較可能です。2つの配列値は、それらの対応する要素が等しい場合、等しいです。

ダイナミック・タイプが同一の 2 つのインターフェイス値を比較すると、そのタイプの値が比較できない場合、ランタイム・パニックが発生します。この動作は、直接的なインタフェース値の比較だけでなく、インタフェース値の配列やインタフェース値のフィールドを持つ構造体を比較する場合にも適用されます。

スライス、マップ、および関数の値は比較できません。ただし、特別な場合として、スライス、マップ、関数の値を事前に宣言された識別子 nil と比較することができる。ポインター、チャネル、インタフェースの値をnilと比較することも許されており、上記の一般的なルールに従っている。

const c = 3 < 4 // c は型付けされていない真理値定数 true

type MyBool bool
var x, y int
var (
	// 比較の結果は型付けされていない真理値です。
	// 通常の代入規則が適用されます。
	b3 = x == y // b3 の型は bool
	b4 bool = x == y // b4 の型は bool
	b5 MyBool = x == y // b5 の型は MyBool
)

論理演算子

[編集]

論理演算子(Logical operators)は、真理値に適用され、オペランドと同じ型の結果を得ることができます。右のオペランドは短絡評価されます[18]

論理演算子
&& 条件付きAND p && q もし p なら q さもなくば false
|| 条件付きOR p || q もし p なら true さもなくば q
! 論理否定 !p もし p なら false さもなくば true

アドレス演算子

[編集]

T型のオペランドxに対して、アドレス演算子(Address operators)&xは、*T型のxへのポインターを生成します。 オペランドはアドレス可能でなければなりません。 すなわち、変数、ポインター・インダイレクト、スライス・インデックス操作、アドレス可能な構造体オペランドのフィールド・セレクタ、アドレス可能な配列の配列インデックス操作のいずれかでなければなりません[19]。 アドレス指定要件の例外として、xは(括弧で囲まれた)複合リテラルでもよい。xの評価がランタイム・パニックを引き起こす場合は、&xの評価も同様です。

xがnilの場合、*xを評価しようとするとランタイム・パニックが発生します。

&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)

var x *int = nil
*x   // ランタイム・パニックを起こす
&*x  // ランタイム・パニックを起こす

受信演算子

[編集]

チャンネル型のオペランドchに対して、受信演算子(Receive operator)<-chの値は、チャンネルchから受信した値となる[20]。チャネルの方向は受信操作を許可しなければならず、受信操作のタイプはチャネルの要素タイプです。式は、値が利用可能になるまでブロックします。nilチャネルからの受信は永遠にブロックされます。閉じたチャネルでの受信操作は、常に即座に進めることができ、それまでに送信された値がすべて受信された後に、要素タイプのゼロ値が得られます。

 v1 := <-ch
 v2 := <-ch
 f(<-ch)
 <-strobe // クロックパルスまで待ち、受信した値を破棄する

特殊な形式の代入や初期化で使われる受信式

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

は、通信が成功したかどうかを報告する型付けされていない真理値の結果を追加します。 okの値は、受信した値がチャネルへの送信操作が成功して配信されたものであればtrue、チャネルが閉じていて空であるために生成されたゼロの値であればfalseである。

変換演算子

[編集]

変換演算子(Conversions)は、式の型を、変換によって指定された型に変更します。変換は、ソースの中で文字通りに現れるかもしれませんし、式が現れる文脈によって暗示されるかもしれません[21]

明示的な変換は、T(x)という形式の式で、Tは型であり、xはT型に変換できる式である。

構文
Conversion = Type "(" Expression [ "," ] ")" ;

型が演算子*や<-で始まる場合、あるいは型がキーワードfuncで始まり結果リストを持たない場合は、曖昧さを避けるために必要に応じて括弧を付けなければなりません。

*Point(p)       // *(Point(p))と同じです。
(*Point)(p)     // pは*Pointに変換される
<-chan int(c)   // 同じく <-(chan int(c))
(<-chan int)(c) // c は <chan int> に変換されます。
func()(x)       // 関数の署名 func() x
(func())(x)     // xはfunc()に変換される
(func() int)(x) // xはfunc() intに変換される
func() int(x)   // xはfunc()intに変換される(曖昧ではない)

定数値xは、xがTの値で表現できる場合、T型に変換することができます。特殊なケースとして、整数の定数xは、非定数xの場合と同じ規則を使って、明示的に文字列型に変換することができます。

定数を変換すると、結果として型付きの定数が得られます。

uint(iota)              // uint型のiota値
float32(2.718281828)    // float32型の2.718281828
complex128(1)           // complex128型の1.0 + 0.0i
float32(0.49999999)     // float32型の0.5
float64(-1e-1000)       // float64型の0.0
string('x')             // string型の "x"
string(0x266c)          // "♬" 型の文字列
MyString("foo" + "bar") // MyString型の "foobar"
string([]byte{'a'})     // 定数ではありません:[]byte{'a'}は定数ではありません
(*int)(nil)             // 定数ではありません: nil は定数ではなく、*int は真偽値、数値、文字列のいずれの型でもありません
int(1.2)                // 違法: 1.2はintとして表現できない
string(65.0)            // 違法: 65.0 は整数型の定数ではない

非定数値xは、これらのいずれかの場合にT型に変換することができます。

  • xT に代入可能
  • structタグ(以下を参照)を無視して、xの型とTは同一の基礎型を持っている
  • structタグ(以下を参照)を無視して、xの型とTは定義された型ではないポインタ型であり、それらのポインタの基本型は同一の基礎型を持っている
  • xの型とTがともに整数型または浮動小数点型
  • xの型とT が共に複素数型
  • x は整数、またはバイトやルーンのスライスで、Tが文字列型
  • x は文字列で、T はバイトまたはルーンのスライス
  • x はスライス、T は配列へのポインタであり、スライス型と配列型は同一の要素型

変換のために構造体タイプの同一性を比較する場合、構造体タグは無視されます。

type Person struct {
	Name    string
	Address *struct {
		Street string
		City   string
	}
}

var data *struct {
	Name    string `json:"name"`
	Address *struct {
		Street string `json:"street"`
		City   string `json:"city"`
	} `json:"address"`
}

var person = (*Person)(data) // タグを無視しても、基本的な型は同じです。

特定の規則は、数値型間や文字列型との間の(非定数)変換に適用されます。これらの変換は、xの表現を変更する可能性があり、実行時のコストが発生します。他のすべての変換は、型を変えるだけで、xの表現を変えません。

ポインタと整数の間を変換する言語的なメカニズムはありません。unsafe パッケージは、制限された状況下でこの機能を実装しています。

数値型間の変換

[編集]

数値型間の変換(Conversions between numeric types)

定数でない数値の変換には、以下のルールが適用されます。

  1. 整数型間の変換では、値が符号付き整数であれば、暗黙の無限精度に符号拡張され、そうでなければゼロ拡張されます。そして、結果の型のサイズに収まるように切り捨てられます。例えば、v := uint16(0x10F0)であれば、uint32(int8(v)) == 0xFFFFFFF0となります。この変換では、常に有効な値が得られ、オーバーフローの表示はありません。
  2. 浮動小数点数を整数に変換する場合、端数は捨てられます(0に向かって切り捨てられます)。
  3. 整数や浮動小数点数を浮動小数点型に、複素数を別の複素数型に変換する場合、結果の値は、変換先の型で指定された精度に丸められます。例えば、float32型の変数xの値は、IEEE754の32ビット数よりも高い精度で格納されている可能性がありますが、float32(x)はxの値を32ビット精度に丸めた結果を表します。同様に、x + 0.1は32ビット以上の精度を使ってもよいが、float32(x + 0.1)はそうではない。

浮動小数点または複素数の値を含むすべての非定数変換において、結果の型がその値を表現できない場合、変換は成功しますが、結果の値は実装に依存します。

文字列型との間の変換

[編集]

文字列型との間の変換(Conversions to and from a string type)

  1. 符号付きまたは符号なしの整数値を文字列型に変換すると、その整数値の UTF-8 表現を含む文字列が得られます。有効なUnicodeコードポイントの範囲外の値は、"\uFFFD"に変換されます。
    string('a')       // "a"
    string(-1)        // "\ufffd" == "\xef\xbf\xbd"
    string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    type MyString string 
    MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
    
  2. スライスされたバイトを文字列に変換すると、スライスされたバイトが連続している文字列になります。
    string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    string([]byte{})                                     // ""
    string([]byte(nil))                                  // ""
    
    type MyBytes []byte
    string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
    
  3. 文字列型の値をルーン型のスライスに変換すると、文字列の個々の Unicode コードポイントを含むスライスが得られます。
    string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    string([]rune{})                         // ""
    string([]rune(nil))                      // ""
    
    type MyRunes []rune 
    string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    
  4. 文字列型の値をバイト型のスライスに変換すると、文字列のバイトを連続した要素とするスライスが得られます。
    []byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    []byte("")        // []byte{}
    
    MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    
  5. 文字列型の値をルーン型のスライスに変換すると、その文字列の個々のUnicodeコードポイントを含むスライスが得られます。
    []rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
    []rune("")                 // []rune{}
    
    MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}
    

スライスから配列ポインタへの変換

[編集]

スライスから配列ポインタへの変換(Conversions from slice to array pointer)

スライスを配列ポインタに変換すると、スライスの基礎となる配列へのポインタが得られます。スライスの長さが配列の長さよりも小さい場合、ランタイム・パニックが発生します。

s := make([]byte, 2, 4)
s0 := (*[0]byte)(s)      // s0 != nil
s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

var t []string
t0 := (*[0]string)(t)    // t0 == nil
t1 := (*[1]string)(t)    // panics: len([1]string) > len(t)

u := make([]byte, 0) 
u0 := (*[0]byte)(u)       // u0 != nil

定数式

[編集]

定数式(Constant expressions)は、定数のオペランドのみを含み、コンパイル時に評価されます[22]

型付けされていない論理値、数値、文字列の各定数は、論理値、数値、文字列の各型のオペランドを使用することが可能な場所であれば、オペランドとして使用できます。

定数比較では、常に型付けされていない真偽値定数が得られます。定数シフト式の左オペランドが型付けされていない定数の場合、その結果は整数定数になりますが、そうでない場合は左オペランドと同じ型の定数になります。

型付けされていない定数に対するその他の操作は、同じ種類の型付けされていない定数、つまり、真偽値、整数、浮動小数点、複素数、または文字列定数になります。二項演算(シフトを除く)の型付けされていないオペランドが異なる種類の場合、結果はこのリストの後の方に出てくるオペランドの種類(整数、ルーン、浮動小数点、複素数)になります。例えば、型付けされていない整数定数を型付けされていない複素数定数で割ると、型付けされていない複素数定数になります。

const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
const b = 15 / 4           // b == 3     (untyped integer constant)
const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
const d = 1 << 3.0         // d == 8     (untyped integer constant)
const e = 1.0 << 3         // e == 8     (untyped integer constant)
const f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)
const g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)
const h = "foo" > "bar"    // h == true  (untyped boolean constant)
const j = true             // j == true  (untyped boolean constant)
const k = 'w' + 1          // k == 'x'   (untyped rune constant)
const l = "hi"             // l == "hi"  (untyped string constant)
const m = string(k)        // m == "x"   (type string)
const Σ = 1 - 0.707i       //            (untyped complex constant)
const Δ = Σ + 2.0e-4       //            (untyped complex constant)
const Φ = iota*1i - 1/1i   //            (untyped complex constant)

型付けされていない整数、ルーン、または浮動小数点の定数に組み込み関数complexを適用すると、型付けされていない複素数の定数が得られます。

const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
const  = complex(0, Θ)   // iΘ == 1i     (type complex128)

定数式は常に正確に評価されますが、中間値や定数自体は、言語で事前に宣言された型がサポートするよりもはるかに大きな精度を必要とする場合があります。以下は合法的な宣言です。

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant) 
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

定数除算または剰余演算の除数は、ゼロであってはなりません。

3.14 / 0.0   // illegal: division by zero

型付けされた定数の値は、常に定数の型の値で正確に表現できなければなりません。以下の定数表現は不正です。

uint(-1)     // -1 cannot be represented as a uint
int(3.14)    // 3.14 cannot be represented as an int
int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300   // operand 300 cannot be represented as an int8 (type of Four) 
Four * 100   // product 400 cannot be represented as an int8 (type of Four)

単項のビットごとの補数演算子^が使用するマスクは、非定数のルールに一致します。マスクは、符号なしの定数ではすべて1、符号付きおよび型付けされていない定数では-1になります。

^1         // untyped integer constant, equal to -2
uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // same as int8(-2)
^int8(1)   // same as -1 ^ int8(1) = -2
実装上の制限
コンパイラーは、型付けされていない浮動小数点または複素数の定数式を計算する際に丸めを使用することがあります。これについては、「定数」のセクションの実装上の制限を参照してください。この丸め処理により、浮動小数点定数式は、無限精度で計算すると整数になる場合でも、整数のコンテキストでは無効になる場合があります(その逆も同様です)。

評価順序

[編集]

評価順序(Order of evaluation)

パッケージレベルでは、初期化依存性により、変数宣言の個々の初期化式の評価順序が決定されます。それ以外では、式、代入、または return 文のオペランドを評価する際に、すべての関数呼び出し、メソッド呼び出し、および通信操作が語彙的に左から右の順で評価されます[23]

例えば、(関数ローカルの)代入では

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

関数の呼び出しと通信は、f()、h()、i()、j()、<-c、g()、k()の順に起こります。しかし、xの評価とインデックス付け、yの評価と、イベントの順序は指定されていません。

a := 1
f := func() int { a++; return a }
x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified 
n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified

関数の呼び出しは、u()、sqr()、v()、f()、v()、g()の順に行われます。

1 つの式の中の浮動小数点演算は、演算子の連想性に従って評価されます。明示的な括弧は、デフォルトの連想性をオーバーライドして評価に影響を与えます。x + (y + z) という式では、y + z の加算が x の加算の前に行われます。

脚註

[編集]
  1. ^ “Expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Expressions. 
  2. ^ “Operands¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Operands. 
  3. ^ “Qualified_identifiers¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Qualified_identifiers. 
  4. ^ “Composite literals¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Composite_literals. 
  5. ^ “Function literals¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Function_literals. 
  6. ^ “Primary expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Primary_expressions. 
  7. ^ “Selectors¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Selectors. 
  8. ^ “Method expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Method_expressions. 
  9. ^ “Method values¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Method_values. 
  10. ^ “Index expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Index_expressions. 
  11. ^ “Slice expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Slice_expressions. 
  12. ^ “Type assertions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Type_assertions. 
  13. ^ “Calls¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Calls. 
  14. ^ “Passing arguments to ... parameters¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Passing_arguments_to_..._parameters. 
  15. ^ “Operators¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Operators. 
  16. ^ 16.0 16.1 16.2 16.3 16.4 “Arithmetic operators¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Arithmetic_operators. 
  17. ^ “Comparison operators¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Comparison_operators. 
  18. ^ “Logical operators¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Logical_operators. 
  19. ^ “Address operators¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Address_operators. 
  20. ^ “Receive operator¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Receive_operator. 
  21. ^ “Conversions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Conversions. 
  22. ^ “Constant expressions¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Constant_expressions. 
  23. ^ “Order of evaluation¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Order_of_evaluation.