BackEnd

Go言語の基礎〜基本構文その1〜

投稿日:2018年10月10日 更新日:

はじめに

こんにちは、nukkyです。

今回はGo言語の連載2回目です。
普段はアプリエンジニアでiOS時々Androidなのですが、今回RE:ENGINESでGo言語の勉強会を行い、せっかくの機会なので自分の知見を広げるためにも今回のブログはGo言語の基礎、基本文法を紹介していこうと思います。

変数

変数の定義

Goにおける全ての変数には「型」を備えます。「型」の詳細については後述します。
早速、変数を宣言してみます。

予約語である「var」の後に「変数の名前」を指定し、最後に「変数の型」を指定します。「var」を使用する場合は変数の名前と型の両方を明示的に指定して変数を定義する必要があります。
同じ型の変数であれば次のように変数をまとめて定義することも可能です。

次のように「var」以下の内容を()で囲うことで異なる型の変数をまとめて定義することも可能です。変数定義を行うブロックが見やすくなるメリットがあります。

暗黙的な定義

関数の中では上記「var」の代わりに演算子「:=」を使用した暗黙的な定義ができます。暗黙的な定義は型の指定が必要ないという特徴を持ちます。

このように、演算子「:=」を使用することで変数の型の定義と値の代入をまとめて行えます。型指定は見当たりませんが上記のように整数1を値として代入すると、変数iの型は暗黙的にint型であると推論されます。このような機能は一般に「型推論」と呼ばれます。

varと暗黙的な定義

「var」を用いた定義でも初期化子を与えることで型を省略することができます。

なお、関数の外で変数を定義する場合は、var を使用する必要があります。

ローカル変数とパッケージ変数

Goの変数は定義される場所によって2種類に分かれます。任意の関数の中に定義された変数が「ローカル変数」で、関数定義の外部に定義された変数を「パッケージ変数」になります。ローカル変数と異なりパッケージ変数は文字通りパッケージに所属する変数です。次のコードを見てください。

パッケージ変数のnは、mainパッケージの中であればどこからでも参照することができます。パッケージ変数はローカル変数とは違い、プログラム全体で一つの値が共有されることに注意してください。

また、後述のスコープで説明しますが、パッケージ変数は、識別子の先頭を大文字にすることで、他のパッケージから参照を可能にすることもできます。名前空間付きのグローバルな変数と同じ扱いになりますので、用法には十分注意をしてください。

定数

const

Goでは予約語「const」を使用して定数の定義が可能です。

「var」による変数の定義と同様に「()」で複数の定数をまとめて定義できます。

次のコードでは定数YとZの値が省略されてますがコンパイルエラーにはなりません。はじめに定義されている定数Xの値がそのまま以降の定数にも割り当てられます。

定数の値を省略しつつ途中で別の値をもつ定数を定義した場合は、その新しい値が以後の定数の暗黙的な値に切り替わります。

iota

GoにはCやJavaにおける「列挙型(enum)」のような機能はありません。しかし、定義済み識別子「iota」を定数定義と組み合わせて使用することで、Cにおける列挙型に近い振る舞いを実現できます。

iotaを使用した後で定数の値を省略すると暗黙的にiotaが繰り返されます。

 

関数

関数定義の基本

2つのint型の値を足し合わせるだけの単純な関数を定義して見ます。

関数は予約語「func」を使用して定義します。関数定義の書き方にもバリエーションがあるのですが、基本形は次の通りです。

Goでは戻り値を持たない関数を定義することもできます。次のコードのように、単に戻り値の型定義を省略するだけです。

複数の戻り値

Goの関数は複数の戻り値を返すことができます。次のコードを見てください。

複数の戻り値を返す関数では、1つ1つの戻り値の型を「(int, int)」のように「()」で囲って、すべて列挙します。なお、次のように「()」を省略すると、コンパイルエラーが発生します。

関数が複数の戻り値を返すのであれば、呼び出し元は複数の戻り値を受け取る必要があります。代入先の変数をカンマで区切って並べ、関数が返す複数の戻り値をそれぞれの変数に割り振って代入することができます。

関数が複数の戻り値を返すからといって、そのすべてに変数に割り当てる必要はありません。次のように、「_」を使って戻り値の一部を破棄することが出来ます。

上記の通り全ての戻り値を破棄することは出来ません。

関数の名前付き戻り値

Goでは、戻り値にあらかじめ名前を付けることができます。先ほどの関数の戻り値に、次のように名前を付けてみます。

名前付き戻り値は、関数内ではゼロ値で初期化された変数として扱うことができます。また、変数に名前を付けている場合は、returnのあとに返す値を明示する必要がなく、returnされた時点での名前付き戻り値の値が自動的に返されることになります。
これを用いると、先の関数は次のように書くことができます。

名前付き戻り値を用いることで、関数の宣言から戻り値の意味が読み取りやすくなると同時に、戻り値のための変数の初期化が不要になり、同じ型の戻り値が多かった場合のreturnの書き間違えなどを防ぐこともできます。

関数とエラー処理

Goには例外機構がありません。つまり、任意の関数を呼び出した場合に、その処理が成功したかどうかを何らかの形で検知する必要があります。Goでは複数の戻り値を返すことができるという特性を利用して、エラーが発生したかどうかを戻り値の一部で示します。

関数「doSomething」は2つの戻り値を返し、変数「err」で受け取ってる2番目の戻り値はエラー発生の有無を表しています。このような書き方は、Goにおける一種のイディオムであり、頻出する表現です。エラー内容を割り当てる変数名がerrなのも慣例的な決まりごとなので、Goのエラー処理を書く場合は、できるだけこの形式に従うべきでしょう。

無名関数

ここまでは明示的に名前を与えられた関数の定義について確認してきましたが、それとは別にGoには「無名関数」という機能が用意されています。これは関数というものをある種の「値」として表現したものと見なせます。関数を値として表現できるのであれば、ある関数が関数を引数にとることも、関数を返す関数を書くことも自在にできます。
次のコードでは、変数fに「intの引数x,yをとりint型を返す無名関数」を代入しています。変数fは名前付きで定義された通常の関数と同様のもので、「f(2, 3)」のように引数を渡して呼び出すことができます。

関数リテラルは次のように記述します。関数名が与えられないところを除けば、名前付き関数の定義方法と同様の書き方です。

関数リテラルを使用して生成された無名関数はどのような型を持つのでしょうか。先ほどと同じ無名関数を書式指定子「%T」をつかって型を調べて見ます。

上記のコードから得られた出力は「func(int, int) int」というものでした。これが「intの引数を2つとりint型の戻り値を返す関数」の型を表します。こちらは型になるので明示的に変数を定義することもできます。ただGoには型推論があるので、わざわざこのような書き方を選ぶメリットはないですが、暗黙的な変数定義によって隠蔽された構造がこのような形式になっていることを理解してください。

関数を返す関数

次のようにして、「関数を返す関数」を定義することができます。

関数returnFuncは、引数をとらず「引数も戻り値もない関数」を戻り値として返す関数として定義されています。

変数を経由せずに、returnFuncの戻り値である関数を、そのまま直接呼び出すこともできます。Goではよくある書き方ですので押さえておきましょう。

スコープ

スコープとは

Goのプログラムコードの任意の場所で、どのような定数や関数が参照可能であるかどうかは、すべて「スコープ」によって決定されます。Goにおけるスコープは大きい単位から「パッケージ」「ファイル」「関数」「ブロック」「制御構文(if、forなど)」によって決定されます。
とくに、定義済み識別子を除いた、関数・変数・定数・型といったプログラムの構成要素はすべてパッケージに属するため、パッケージのスコープを理解することは「要素の可視性」のコントロールのためにも重要です。

パッケージのスコープ

Goのプログラムは複数のパッケージを組み合わせて構成されます。各パッケージ間で定数や関数を共有するために、パッケージ下に定義された識別子を他のパッケージからも参照できるようするのか、内部のみで利用する識別子にして隠蔽するのか、それぞれの識別子をコントロールする必要があります。
パッケージに定義された定数、変数、関数などが他のパッケージから参照可能であるかは、「識別子の1文字目が大文字」であるかどうかで決定されます。

なお識別子1文字目が日本語など大文字、小文字がない文字である場合には小文字で定義したのと同様に他のパッケージから参照できません。

ファイルのスコープ

Goでは1つのパッケージを定義するために複数のGoファイルを使用できます。パッケージ定義が複数のファイルによって構成されている場合、import宣言は各々のファイルのみ有効になります。
同一パッケージであればファイルが分割されていても、定数や関数といった要素に関しては相互に参照可能ですが、import宣言のみ独立していることに注意してください。

関数のスコープ

Goの関数はスコープを構成します。関数の中で定義された変数や定数は、定義された関数の中でのみ参照可能です。
関数のスコープには、引数の変数と戻り値の変数も含まれています。次のように同名の識別子を関数内で改めて定義しようとすると、コンパイルエラーが発生します。

関数の中には「{}」を使って明示的に別のブロックを定義できます。ブロックを分けることで関数スコープにある識別子と重複する識別子を使用できます。

ここで定義されている変数numは「{}」ブロック内でしか参照できないので注意してください。

Goの基本型

Goは「静的型付け言語」です。すべての変数は何らかの型に属し、異なる型同士の演算といった問題点の多くはコンパイル時に検出されます。
以下がGoの基本型の一覧になります

説明
uint8 8ビット符号なし整数
uint16 16ビット符号なし整数
uint32 32ビット符号なし整数
uint64 64ビット符号なし整数
int8 8ビット符号あり整数
int16 16ビット符号あり整数
int32 32ビット符号あり整数
int64 64ビット符号あり整数
float32 32ビット浮動小数
float64 64ビット浮動小数
complex64 64ビット複素数
complex128 128ビット複素数
byte uint8のエイリアス
string 文字列型
rune Unicodeのコードポイント
uint 32か64ビットの符号なし整数
int 32か64ビットの符号あり整数
uintptr ポインタ値用符号なし整数
bool 論理値型
error エラーを表わすインタフェース

型キャスト

Goでは、暗黙的な型変換が起こることはありません。しかし型を変換できないわけではなく、キャストで明示的に変換することができます。
キャストは、キャストしたい型を指定して次のように行います。

Goの型変換は「型(値)」のように書くことができます。

型アサーション

どんな型の値でも受け取れるinterface{}ですが、interface{}型の引数で受け渡された値は、元の型の情報が欠落しています。
(interface{}型については基本構文その2で説明します)
Go言語ではこのような局面で利用するための型アサーションを提供しており、型アサーションにより実体の型が何であるかを動的にチェックすることができます。構文は次のような形になります。

基本的に以下のように記述します、1番目の変数には型アサーション成功時に実際の値が格納されます。2番目の変数には型アサーションの成功の有無(true/false)が格納されます。

以下はサンプルの関数です。引数に型アサーションを用いて型に応じた処理の分岐をさせています。

ぱっと見たところ「value」と「ok」が関数内に2回宣言されているように見えますが、ifに記載することでif文のブロック内でしかスコープされていない状態になりコンパイルエラーは発生しません。(この書き方は後述のifで説明します)

制御構文

for

Goに用意されている制御構文は、他のプログラミング言語と比較すると非常にシンプルに構成されています。たとえば、ループを記述するために用意されているのは「for」のみです。

if

ifは条件文を構成します。他のプログラミング言語と同じようにifの後に条件式を記述することでブロックが実行されます。

Goではifの書き方にもう一つバリエーションがあります。「簡易文」を伴う条件文です。簡易文とは「式」や「代入文」、「暗黙の変数定義」などの複雑な構造を持たない単一文のことです。簡易文は条件式の前におかれ「;」で区切られます。

型アサーションで使用したコードを見てみましょう。

これが簡易文付きifになります。簡易文は条件式に先立って評価されます。なので上記のコードだと「value」と「ok」に条件式よりも前に値が入り、論理値「ok」で条件を判断しています。Goではifに類する各制御構文は暗黙的ブロックを構成します。ifの外側で定義された変数と、内側で定義された変数とではスコープが異なります。

switch

if/else文が繰り返す場合は、switch文を用いたほうがスッキリ書ける場合があります。Goのswitch文は非常に柔軟であり、値の比較だけでなく条件分岐にも使用できます。まず、値を用いたswitch文は次のようになります。カンマで区切った複数の値も指定できます。

Goのswitch文では、caseに値だけでなく式も指定できます。

さらにGoのswitch文では、caseに型も指定できます。型アサーションと分岐を組み合わせた処理を手軽に書くことができます。

おまけ:goto

Goでは関数内の任意の位置にジャンプするための「goto」文が用意されています。gotoは使いどころが難しく基本構文その1ではそんなのもあるよ程度に留めておきます。

さいごに

まずは基本構文その1としてGo言語の基礎、基本文法を紹介させていただきました。せっかくの連載なのと自分の熱が高いうちにということで基本構文その2は明日公開します!

Go記事の連載などは、こちらをご覧ください。

おすすめ書籍

スターティングGo言語 (CodeZine BOOKS) Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズ みんなのGo言語【現場で使える実践テクニック】 Go言語による並行処理

blog-page_footer_336




blog-page_footer_336




-BackEnd
-,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

rails

Railsで複合主キーのテーブルを扱う

1 はじめに1.1 前提条件2 実装例2.1 config2.2 マイグレーション2.3 モデル3 さいごに はじめに RailsでWebサービスを開発する際のDB設計では基本的にidが主キーになると ...

Next.jsのrevalidatePathとrevalidateTag

1 はじめに2 前提3 revalidatePathとrevalidateTag3.1 revalidatePath3.2 revalidateTag4 今回の場合5 おすすめ書籍 はじめに Next ...

Go言語

mutexを使ってGoで排他処理をする

1 はじめに1.1 mutexとは2 mutexを使った排他制御2.1 失敗するケース2.2 mutexを使って排他制御した場合2.3 構造体へmutexを埋め込む3 RWMutexを使う4 さいごに ...

rails

私たちのFactoryGirlとRspecの使用ルール その1

1 はじめに2 そもそもなぜ基本ルールなどを作成しようとしたのか?3 Rspec編3.1 describe、context、itの階層で使用する3.2 10個以上のテストレコードを作成したい場合は、ト ...

rails

Capistrano3でRailsアプリケーションをデプロイする

1 はじめに1.1 前提条件2 Cpistranoについて3 導入3.1 Gemのインストール3.2 設定ファイルの準備4 デプロイ設定4.1 Capfileを修正する4.2 各環境で共通のデプロイ設 ...

フォロー

blog-page_side_responsive

2018年10月
 123456
78910111213
14151617181920
21222324252627
28293031  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。