Go/式

式(An expression)は、演算子や関数をオペランドに適用して値を計算することを指定します。
オペランド(Operands)とは、式の中の基本的な値を表すものです。オペランドは、リテラル、定数、変数、関数を表す(修飾されている場合もある)ブランクでない識別子、または括弧で囲まれた式です。
ジェネリック関数を示すオペランド名の後に、型引数のリストを続けることができます;結果として生じるオペランドは、インスタンス化された関数です。 空白の識別子(_)は,代入の左側においてのみオペランドとして現れることができます。
修飾された識別子(Qualified identifiers)とは、パッケージ名のプレフィックスで修飾された識別子のことです。パッケージ名と識別子の両方が空白であってはなりません。
修飾された識別子は、別のパッケージにある識別子にアクセスするもので、インポートされなければなりません。識別子は、そのパッケージのパッケージブロックにエクスポートして宣言する必要があります。
複合リテラル(Composite literals)は、評価されるたびに新しい複合値を構築します。複合リテラルは、リテラルの型に続いて、要素の中括弧付きリストで構成されます。各要素の前には、対応するキーを付けることができます。
LiteralType のコア型 T は、構造体、配列、スライス、またはマップ型でなければなりません (文法では、型が TypeName として指定される場合を除き、この制約が適用されます)。 要素とキーの型は、型Tのそれぞれのフィールド、要素、キー型に割り当て可能でなければなりません。 キーは、構造体リテラルではフィールド名として、配列およびスライスリテラルではインデックスとして、マップリテラルではキーとして解釈されます。 マップリテラルの場合、すべての要素にキーが必要です。 同じフィールド名や定数キー値で複数の要素を指定するとエラーになります。
関数リテラル(A function literal )は、匿名関数を表します。
関数リテラルは、変数に代入したり、直接呼び出すことができます。
関数リテラルはクロージャであり、周囲の関数で定義された変数を参照することができます。これらの変数は、周辺の関数と関数リテラルの間で共有され、アクセス可能である限り存続します。
一次式(Primary expressions)は、単項式や二項式のオペランドになります。
セレクター(Selectors)
次の x がパッケージ名でない一次式であれば、セレクター式です。
は、値xのフィールドまたはメソッドfを表します(*xの場合もあります。 識別子fは、(フィールドまたはメソッド)セレクターと呼ばれ、ブランク識別子であってはなりません。 xがパッケージ名の場合は、修飾された識別子のセクションを参照してください。
セレクターfは、型Tのフィールドやメソッドfを示すこともあれば、Tの入れ子になった埋め込みフィールドのフィールドやメソッドfを参照することもあります。 fに到達するまでに走査された埋め込みフィールドの数を、Tにおける深さ(The depth)と呼びます。 Tの埋め込みフィールドAで宣言されたフィールドまたはメソッドfの深さは、Aのfの深さに1を加えたものです。
セレクタには以下のルールが適用されます。
メソッド式(Method expressions)
Mが型Tのメソッドの集合にある場合、T.Mは、Mと同じ引数にメソッドのレシーバーである追加の引数を前置した通常の関数として呼び出し可能な関数である。
T型の構造体に、T型のレシーバーを持つMvと、*T型のレシーバーを持つMpという2つのメソッドがあるとします。
は、Mvと同等の関数を生成しますが、第1引数に明示的なレシーバーを指定します。これは、
この関数は通常、明示的なレシーバーを使って呼び出すことができるので、これらの5つの呼び出しは同等です。
同様に、
のシグネチャーを持つMpを表す関数値が得られます。
値のレシーバーを持つメソッドでは,明示的なポインターのレシーバーを持つ関数を派生させることができます.
というシグネチャを持つMvを表す関数値が得られます。
このような関数は、レシーバーを介して間接的に値を生成し、基礎となるメソッドにレシーバーとして渡します。このメソッドは、関数呼び出しでアドレスが渡された値を上書きしません。
ポインター・レシーバー・メソッドに対する値レシーバー関数という最後のケースは、ポインター・レシーバー・メソッドが値型のメソッド・セットに含まれていないため、違法です。
メソッドから派生した関数値は、関数呼び出し構文で呼び出され、レシーバーは呼び出しの第一引数として提供されます。つまり、f := T.Mvが与えられた場合、fはt.f(7)ではなくf(t, 7)として呼び出されます。レシーバーを束縛する関数を構築するには、関数リテラルまたはメソッド値を使用します。
インターフェイス型のメソッドから関数値を派生させることは合法です。結果として得られる関数は、そのインタフェース型の明示的なレシーバーを取ります。
メソッドの値(Method values)
式xが静的型Tを持ち、Mが型Tのメソッドセットにある場合、x.Mはメソッド値と呼ばれる。メソッド値x.Mは、x.Mのメソッド呼び出しと同じ引数で呼び出し可能な関数値である。式xはメソッド値の評価中に評価されて保存され、保存されたコピーは後に実行される可能性のある呼び出しのレシーバーとして使用される。
型Tはインターフェイス型でも非インターフェイス型でもよい。
前述のメソッド式の説明と同様に、型Tの構造体に2つのメソッドMv(レシーバーがT型)とMp(レシーバーが*T型)があるとします。
次の2つの呼び出しは等価です。
同様に、
セレクタと同様に、ポインタを使った値のレシーバを持つ非インターフェイスのメソッドを参照すると、そのポインタは自動的に参照解除されます:pt.Mvは(*pt).Mvと同等です。
メソッドコールと同様に、アドレス指定可能な値を使用したポインターレシーバを持つ非インターフェースメソッドへの参照は、自動的にその値のアドレスを取ります:t.Mpは(&t).Mpと同等です。
上記の例では非インターフェイス型を使用していますが、インターフェイス型の値からメソッドの値を生成することも合法です。
インデックス式(Index expressions)
は、配列の要素、配列へのポインタ、スライス、文字列、マップのうち、x でインデックスされたものを表し、以下のルールが適用されます。
map[K]V型のマップaに対するインデックス式で、特別な形式の代入または初期化で使用されます。
は、追加の型付けされていない真理値をもたらします。ok の値は、キー x がマップ内に存在する場合は true、そうでない場合は false です。
nilマップの要素に代入するとランタイムパニックを起こします。
スライス式(Slice expressions)は、文字列、配列、配列へのポインタ、スライスから部分文字列やスライスを構成します。下限と上限を指定する単純な形式と、容量の上限も指定する完全な形式の2つのバリエーションがあります。
単純なスライス式(Simple slice expressions)
文字列、配列、配列へのポインタ、またはスライス a に対して、
は、部分文字列またはスライスを構成します。添字 low と high は、オペランド a のどの要素が結果に現れるかを選択します。結果は0から始まるインデックスを持ち、長さはhigh - lowになります。配列aをスライスした後
このスライスsは、型が[]int、長さが3、容量が4、そして要素が
便宜上、いずれかのインデックスを省略することができます.低位のインデックスがない場合のデフォルトはゼロで、高位のインデックスがない場合のデフォルトはスライスされたオペランドの長さとなります。
aが配列へのポインタの場合、a[low : high]は(*a)[low : high]の省略形です.
配列や文字列の場合、0 <= low <= high <= len(a) であればインデックスは範囲内にあり、そうでなければ範囲外となります.スライスの場合、インデックスの上限は、長さではなく、スライスの容量cap(a)です。定数インデックスは、非負であり、int型の値で表現できなければならない。配列や定数文字列の場合、定数インデックスも範囲内でなければならない。配列や定数文字列の場合、定数インデックスも範囲内でなければなりません。また、両方のインデックスが定数の場合、low <= highを満たさなければなりません。実行時にインデックスが範囲外の場合、ランタイムパニックが発生します。
型付けされていない文字列を除き、スライスされたオペランドが文字列またはスライスの場合、スライス操作の結果はオペランドと同じ型の非定数値になります。型付けされていない文字列オペランドの場合、結果はstring型の非定数値となります。スライスされたオペランドが配列の場合、それはアドレス可能でなければならず、スライス操作の結果は、配列と同じ要素タイプのスライスとなります。
有効なスライス式のスライスされたオペランドが nil スライスの場合、結果は nil スライスになります。それ以外の場合、結果がスライスであれば、オペランドと基礎となる配列を共有します。
完全なスライス式(Full slice expressions)
配列、配列へのポインタ、スライスa(文字列は不可)の場合、一次式は
は、単純なスライス式a[low : high]と同じ長さと要素を持つ、同じ型のスライスを構築します。さらに、結果として得られるスライスの容量を max - low に設定して制御します。最初のインデックスだけは省略可能で、デフォルトでは0です。
スライスtの型は[]int、長さ2、容量4、要素は
単純なスライス式としては、aが配列へのポインタの場合、a[low : high : max]は(*a)[low : high : max]の省略形となります。スライスされたオペランドが配列の場合は、アドレス指定可能でなければなりません。
添字は、0 <= low <= high <= max <= cap(a) であれば範囲内、そうでなければ範囲外となります。定数のインデックスは、非負で、int型の値で表現できなければなりません。複数のインデックスが定数の場合、存在する定数はお互いに範囲内でなければなりません。実行時にインデックスが範囲外である場合、ランタイム・パニックが発生します。
型アサーション(Type assertions)
インターフェイス型の式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であることがわかっているのです。
特殊な形式の代入または初期化で使用されるタイプアサーション
は、追加の型付けされていないブール値を出力します。 アサーションが成立していれば ok の値は真です。 それ以外の場合は偽となり、vの値はT型のゼロ値となります。 この場合、ランタイム・パニックは発生しません。
呼び出し(Calls)
関数型Fの式fが与えられます。
は、引数 a1, a2, ... an で f を呼び出します。1つの特別な場合を除いて、引数はFのパラメータ型に割り当て可能な単一値の式でなければならず、関数が呼び出される前に評価されます。 式の型はFの結果の型です。メソッドの呼び出しも同様ですが、メソッド自体は、メソッドのレシーバ型の値のセレクタとして指定されます。
関数呼び出しでは、関数値と引数が通常の順序で評価されます。 評価された後、呼び出しのパラメータは値で関数に渡され、呼び出された関数は実行を開始します。 関数の戻りパラメータは、関数が戻るときに呼び出し元に値で返されます。
nilの関数値を呼び出すと、ランタイム・パニックが発生します。
特殊なケースとして、関数やメソッドgの戻り値が同数で、別の関数やメソッドfのパラメータに個別に割り当て可能な場合、f(g(parameters_of_g))という呼び出しは、gの戻り値をfのパラメータに順に束縛した後にfを呼び出すことになります。 fの呼び出しには、gの呼び出し以外のパラメータを含んではならず、gは少なくとも1つの戻り値を持っていなければなりません。 fに最後の...パラメータがある場合は、通常のパラメータを割り当てた後に残ったgの戻り値が割り当てられます。
メソッドコール x.m() は、x の (その型の) メソッドセットに m が含まれ、引数リストが m のパラメータリストに割り当てられる場合に有効です。 x がアドレス可能で、&x のメソッドセットに m が含まれる場合、x.m() は (&x).m() の短縮形となります。
明確なメソッドタイプはなく、メソッド・リテラルも存在しません。
仮引数に ... パラメーターを渡す(Passing arguments to ... parameters)
fが、最終パラメータpが...T型のバリアディック型である場合、f内ではpの型は[]T型と同等です。 fがpの実際の引数を持たずに呼び出された場合、pに渡される値はnilです。 そうでなければ、渡される値は[]T型の新しいスライスで、連続した要素が実際の引数である新しい基礎的な配列であり、これらは全てTに割り当て可能でなければなりません。 したがって、スライスの長さと容量はpに束縛された引数の数であり、各呼び出しサイトで異なる可能性があります。
次のような関数と呼び出しがあります。
Greetingの中で、whoは最初の呼び出しではnilという値を持ち、2回目の呼び出しでは[]string{"Joe", "Anna", "Eileen"}という値を持ちます。
最後の引数がスライスタイプ []T に割り当て可能で、その後に ... が続く場合、それは ...T パラメータの値として変更されずに渡されます。この場合、新しいスライスは作成されません。
スライスsとコールが与えられると
を呼び出すと、Greetingの中では、同じ配列を持つsと同じ値を持つことになります。
演算子(Operators)は、は、オペランドを組み合わせて式を作ります。
比較については別の場所で説明します。他の二項演算子では、シフトや型付けされていない定数を含む演算でない限り、オペランドの型は同一でなければなりません。定数のみを扱う演算については、定数式の項を参照してください。
シフト演算を除き、一方のオペランドが型付けされていない定数で、もう一方のオペランドが型付けされていない場合、定数は暗黙のうちにもう一方のオペランドの型に変換されます。
シフト式の右オペランドは、整数型であるか、またはuint型の値で表現可能な型付けされていない定数でなければなりません。定数ではないシフト式の左オペランドが型付けされていない定数の場合、まずその左オペランドだけでシフト式を置き換えた場合に想定される型に暗黙のうちに変換されます。
演算子の優先順位(Operator precedence)
単項演算子(UnaryExpr)が最も高い優先順位を持ちます。 ++および--演算子は、式ではなく文を形成するため、演算子の階層から外れます。 そのため、文 *p++ は (*p)++ と同じになります。
二項演算子には5つの優先順位があります。 乗算演算子の優先順位が最も高く、次に加算演算子、比較演算子、&&(論理AND)、最後に||(論理OR)が続きます。
同じ優先順位の二項演算子は、左から右へと関連付けられます。例えば、x / y * z は、(x / y) * z と同じです。
算術演算子(Arithmetic operators)は、数値に適用され、第一オペランドと同じ型の結果を得ることができます。4つの標準的な算術演算子(+, -, *, /) は、整数型、浮動小数点型、複素数型に適用され、+は文字列にも適用されます。ビットごとの論理演算子とシフト演算子は、整数型にのみ適用されます。
整数演算子(Integer operators)
2つの整数値xとyに対して、整数の商 q = x / y {\displaystyle q=x/y} と、剰余 r = x % y {\displaystyle r=x\%y} は以下の関係を満たす。
x / yを0に向かっての切捨て
唯一の例外は、被除数xがint型のxに対して最も負の値である場合、2の補数の整数オーバーフローにより、商q = x / -1はxに等しい(およびr = 0)ということです。
除数が定数の場合は、ゼロであってはなりません。実行時に除数がゼロの場合、ランタイムパニックが発生します。被除数が非負で除数が定数の2乗の場合、除算は右シフトで置き換えられ、余剰の計算はビットごとのAND演算で置き換えられます。
シフト演算子は、左オペランドを右オペランドで指定されたシフトカウントだけシフトします。実行時にシフトカウントが負の値であった場合、ランタイム・パニックが発生します。シフト演算子は、左オペランドが符号付き整数の場合は算術シフトを、符号なし整数の場合は論理シフトを実行します。シフト回数には上限がありません。シフトは、シフトカウントnに対して、左オペランドが1ずつn回シフトされたように動作します。その結果、x << 1 は x*2 と同じで、x >> 1 は x/2 と同じですが、負の無限大に向かって切り捨てられます。
整数のオペランドに対して、単項演算子の+、-、^は以下のように定義されています。
整数のオーバーフロー(Integer overflow)
符号なし整数値の場合、+, -, *, <<の演算は剰余演算2n(nは符号なし整数の型のビット幅)で計算されます。 大雑把に言うと、これらの符号なし整数の演算はオーバーフロー時に上位ビットを捨てるので、プログラムでは「ラップアラウンド」に頼ることになります。
符号付き整数の場合、 +, -, *, /, <<の各演算は合法的にオーバーフローしても構いませんが、結果として得られる値は符号付き整数表現、演算、およびそのオペランドによって決定論的に定義され、存在します。 オーバーフローはランタイム パニックを引き起こしません。 コンパイラは、オーバーフローが発生しないことを前提にコードを最適化することはできません。 例えば、x < x + 1 が常に真であると仮定することはできません。
浮動小数点演算子(Floating-point operators)
浮動小数点および複素数の場合、+xはxと同じで、-xはxの(算術)否定です。浮動小数点または複素数のゼロ除算の結果は、IEEE-754規格以上には規定されておらず、ランタイム・パニックが発生するかどうかは実装に依存します。
実装では、複数の浮動小数点演算を1つの融合した演算にまとめ、場合によっては文をまたいで実行し、丸めて得られる値とは異なる結果を生成することがあります。明示的な浮動小数点型の変換では、対象となる型の精度に合わせて丸めるので、その丸め方を捨ててしまうような融合を防ぐことができます。
たとえば、一部のアーキテクチャでは、中間結果 x*y を丸めずに x*y + z を計算する「融合積和演算」("fused multiply and add"; FMA)命令が提供されています。これらの例では、Goの実装がその命令を使用できる場合を示しています。
文字列の連結(String concatenation)
文字列は、+演算子や+=代入演算子を使って連結することができます。
文字列の加算では、オペランドを連結して新しい文字列を作成します。
比較演算子(Comparison operators)は、2つのオペランドを比較し、型付けされていないブール値を出力します。
どのような比較においても、第1オペランドは第2オペランドの型に代入可能でなければならず、またその逆も同様です。
等式演算子 == および != は、比較可能なオペランドに適用されます。順序付け演算子 <, <=, >, >= は、オペランドが順序付けられている場合に適用されます。これらの用語と比較の結果は次のように定義されます。
ダイナミック・タイプが同一の 2 つのインターフェイス値を比較すると、そのタイプの値が比較できない場合、ランタイム・パニックが発生します。この動作は、直接的なインタフェース値の比較だけでなく、インタフェース値の配列やインタフェース値のフィールドを持つ構造体を比較する場合にも適用されます。
スライス、マップ、および関数の値は比較できません。ただし、特別な場合として、スライス、マップ、関数の値を事前に宣言された識別子 nil と比較することができる。ポインター、チャネル、インタフェースの値をnilと比較することも許されており、上記の一般的なルールに従っている。
論理演算子(Logical operators)は、真理値に適用され、オペランドと同じ型の結果を得ることができます。右のオペランドは短絡評価されます。
T型のオペランドxに対して、アドレス演算子(Address operators)&xは、*T型のxへのポインターを生成します。 オペランドはアドレス可能でなければなりません。 すなわち、変数、ポインター・インダイレクト、スライス・インデックス操作、アドレス可能な構造体オペランドのフィールド・セレクタ、アドレス可能な配列の配列インデックス操作のいずれかでなければなりません。 アドレス指定要件の例外として、xは(括弧で囲まれた)複合リテラルでもよい。xの評価がランタイム・パニックを引き起こす場合は、&xの評価も同様です。
xがnilの場合、*xを評価しようとするとランタイム・パニックが発生します。
チャンネル型のオペランドchに対して、受信演算子(Receive operator)<-chの値は、チャンネルchから受信した値となる。チャネルの方向は受信操作を許可しなければならず、受信操作のタイプはチャネルの要素タイプです。式は、値が利用可能になるまでブロックします。nilチャネルからの受信は永遠にブロックされます。閉じたチャネルでの受信操作は、常に即座に進めることができ、それまでに送信された値がすべて受信された後に、要素タイプのゼロ値が得られます。
特殊な形式の代入や初期化で使われる受信式
は、通信が成功したかどうかを報告する型付けされていない真理値の結果を追加します。 okの値は、受信した値がチャネルへの送信操作が成功して配信されたものであればtrue、チャネルが閉じていて空であるために生成されたゼロの値であればfalseである。
変換演算子(Conversions)は、式の型を、変換によって指定された型に変更します。変換は、ソースの中で文字通りに現れるかもしれませんし、式が現れる文脈によって暗示されるかもしれません。
明示的な変換は、T(x)という形式の式で、Tは型であり、xはT型に変換できる式である。
型が演算子*や<-で始まる場合、あるいは型がキーワードfuncで始まり結果リストを持たない場合は、曖昧さを避けるために必要に応じて括弧を付けなければなりません。
定数値xは、xがTの値で表現できる場合、T型に変換することができます。特殊なケースとして、整数の定数xは、非定数xの場合と同じ規則を使って、明示的に文字列型に変換することができます。
定数を変換すると、結果として型付きの定数が得られます。
非定数値xは、これらのいずれかの場合にT型に変換することができます。
変換のために構造体タイプの同一性を比較する場合、構造体タグは無視されます。
特定の規則は、数値型間や文字列型との間の(非定数)変換に適用されます。これらの変換は、xの表現を変更する可能性があり、実行時のコストが発生します。他のすべての変換は、型を変えるだけで、xの表現を変えません。
ポインタと整数の間を変換する言語的なメカニズムはありません。unsafe パッケージは、制限された状況下でこの機能を実装しています。
数値型間の変換(Conversions between numeric types)
定数でない数値の変換には、以下のルールが適用されます。
浮動小数点または複素数の値を含むすべての非定数変換において、結果の型がその値を表現できない場合、変換は成功しますが、結果の値は実装に依存します。
文字列型との間の変換(Conversions to and from a string type)
スライスから配列ポインタへの変換(Conversions from slice to array pointer)
スライスを配列ポインタに変換すると、スライスの基礎となる配列へのポインタが得られます。スライスの長さが配列の長さよりも小さい場合、ランタイム・パニックが発生します。
定数式(Constant expressions)は、定数のオペランドのみを含み、コンパイル時に評価されます。
型付けされていない論理値、数値、文字列の各定数は、論理値、数値、文字列の各型のオペランドを使用することが可能な場所であれば、オペランドとして使用できます。
定数比較では、常に型付けされていない真偽値定数が得られます。定数シフト式の左オペランドが型付けされていない定数の場合、その結果は整数定数になりますが、そうでない場合は左オペランドと同じ型の定数になります。
型付けされていない定数に対するその他の操作は、同じ種類の型付けされていない定数、つまり、真偽値、整数、浮動小数点、複素数、または文字列定数になります。二項演算(シフトを除く)の型付けされていないオペランドが異なる種類の場合、結果はこのリストの後の方に出てくるオペランドの種類(整数、ルーン、浮動小数点、複素数)になります。例えば、型付けされていない整数定数を型付けされていない複素数定数で割ると、型付けされていない複素数定数になります。
型付けされていない整数、ルーン、または浮動小数点の定数に組み込み関数complexを適用すると、型付けされていない複素数の定数が得られます。
定数式は常に正確に評価されますが、中間値や定数自体は、言語で事前に宣言された型がサポートするよりもはるかに大きな精度を必要とする場合があります。以下は合法的な宣言です。
定数除算または剰余演算の除数は、ゼロであってはなりません。
型付けされた定数の値は、常に定数の型の値で正確に表現できなければなりません。以下の定数表現は不正です。
単項のビットごとの補数演算子^が使用するマスクは、非定数のルールに一致します。マスクは、符号なしの定数ではすべて1、符号付きおよび型付けされていない定数では-1になります。
評価順序(Order of evaluation)
パッケージレベルでは、初期化依存性により、変数宣言の個々の初期化式の評価順序が決定されます。それ以外では、式、代入、または return 文のオペランドを評価する際に、すべての関数呼び出し、メソッド呼び出し、および通信操作が語彙的に左から右の順で評価されます。
例えば、(関数ローカルの)代入では
関数の呼び出しと通信は、f()、h()、i()、j()、<-c、g()、k()の順に起こります。しかし、xの評価とインデックス付け、yの評価と、イベントの順序は指定されていません。
関数の呼び出しは、u()、sqr()、v()、f()、v()、g()の順に行われます。
1 つの式の中の浮動小数点演算は、演算子の連想性に従って評価されます。明示的な括弧は、デフォルトの連想性をオーバーライドして評価に影響を与えます。x + (y + z) という式では、y + z の加算が x の加算の前に行われます。