はじめに
最近あるサイトで、仕事で使われている言語ランキングを見ていたのですが、そのTop10に入っているもののほとんどが、私が触ったことのない言語でした。
これは非常にまずい状況だと思ったので、一通り触ってみて、しっくりきたものを深く勉強していこうと思います。
その第一弾として、Go言語を触ってみました。休日1日分でできるところまで進めた分を記事にします。
私はC#やPythonを仕事で使っているため、これらの知識をベースにして理解を広げます。同じような境遇の方の理解の助けになれば幸いです。
環境構築
環境
OS: Ubuntu22.04
エディタ: vs code
Goのインストール Windowsの場合
-
公式サイトからGoをダウンロードします。
Windowsの型はmsiファイルをダウンロードしてインストールすれば、完了です
Goのインストール Ubuntuの場合
-
公式サイトからGoをダウンロードします。
Goはaptでもインストールできたのですが、バージョンがかなり古くて、vs codeの拡張機能と互換性がありませんでした。公式サイトからダウンロードしましょう。
-
公式サイトの手順に従ってGoをインストールします。
ターミナルにて、ダウンロードしたtar.gzファイルを置いたディレクトリに移動したあと、以下を実行しました。
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.6.linux-amd64.tar.gz
パスを通すため、~.bashrcファイルの最後に以下を追記します。
export PATH=$PATH:/usr/local/go/bin
VS codeの設定
- VS codeでGoの拡張機能をインストールします
拡張機能から検索して、一番上に出てくる"Go"をインストールします。
メニューバーのヘルプ=>コマンドの表示 でコマンドパレットを表示し、Go: install/update tools
を実行します
以上で環境が整いました。
動かしてみる
とりあえずHello World
HelloWorldProjectというディレクトリを作り、GoPractice1.goというファイルを作りました。
構成
HelloWorldProject
├── GoPractice1.go
コード
package main
import "fmt"
func main() {
fmt.Printf("Hello World\n")
}
動かす
このコードを動かすために、まずvs codeのterminalでmodコマンドを実行する必要があります。
go mod init practice
Goはすべてのコードをモジュールとして管理するようです。つまり、今回作ったGoPractice1.goは何かしらのモジュールに属している必要があります。そのため、modコマンドを実行し、モジュール(practiceという名前になる)を新規作成するようです。
参考になる記事:
一度modを実行しておけば、あとはvs codeのデバッグを実行できるようになります。
特徴的なコードを実装してみる
並行処理
Go言語の特徴の一つは、複雑な並行処理をシンプルなコードで実装できることだそうです。
並行処理については、次の記事で分かりやすく説明されています。
Goでは、Goroutineというスレッドが並行処理を上手いことやりくりしてくれるようです。そのおかげで、他の言語に比べて並行処理のコードがシンプルになります。
並行処理を使ったコードの例
次のコードは、func1とfunc2を並行処理で実行しているコードです。どちらの関数も間に1秒の待ち時間を含んでいます。
package main
import (
"fmt"
"sync"
"time"
)
func func1(wg *sync.WaitGroup) {
defer wg.Done() // gorutineを使い終わったことを宣言する処理。
// deferで宣言しておくと、この関数の最後に実行されるようになる。
fmt.Println("Func1 start")
time.Sleep(time.Second) // 1秒待つ
fmt.Println("Func1 end")
// このあとにdeferで宣言したwg.Done()が実行される
}
func func2(wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("\t\tFunc2 start")
time.Sleep(time.Second) // 1秒待つ
fmt.Println("\t\tFunc2 end")
}
func main() {
// WaitGroupを作成して、goroutineを使えるようにする。
var wg sync.WaitGroup
wg.Add(2) // goroutineを2つ分使うための処理。
go func1(&wg)
go func2(&wg)
// すべてのgoroutineが終了するのを待つ
wg.Wait()
fmt.Println("すべての処理が完了しました。")
}
出力は次のようになりました。
Func2 start => Func1 start => Func1 end => Func2 end の順番で実行されています。
Func2 startのあとには1秒の待機時間を入れていましたが、その待機中にFunc1 startが実行されていたことが分かります。
インターフェースとその実装
Go言語でもインターフェースが使えます。ただし、C#やJavaとは実装の仕方がかなり異なります。
とにかく実装してみる
Goには組み込みでerrorインターフェースが用意されています。エラー処理の実装時に使われるインターフェースです。
type error interface {
Error() string // Errorというメソッドを持つものがerrorインターフェース
}
では、このerrorインターフェースを実装したいと思います。
C#などでは、クラスを用意し、そこに実装します。しかし、Goにはクラスがありません。
構造体に実装
そこで、構造体に実装します。
PracticeErrorという構造体を作りました。
type PracticeError struct {
Message string
}
このPracticeErrorにerrorインターフェースを実装します。
errorインターフェースは、Errorメソッド一つを持っているだけです。そのため、PracticeErrorにError()の実装をするだけで、PracticeErrorはerrorインターフェースを実装するとみなされます。
func (e PracticeError) Error() string {
// "Error. Message : {Messageの内容}" という文字列を返す
return "Error. Message : " + e.Message
}
このように、メソッド内のどこにもerror
インターフェースの文字は出てきません。
このような手法を"ダックタイピング"と言うそうです。
C#に慣れていると、PracticeErrorとerrorインターフェースが紐付いていることがわかるようなコードを書きたくなりますね…
これを動かしてみます。
package main
import (
"fmt"
)
type PracticeError struct {
Message string
}
func (e PracticeError) Error() string {
return "Error. Message : " + e.Message
}
func main() {
// PracticeErrorはerrorインターフェースを実装したため、error型として宣言できる。
var practiceError error = PracticeError{Message: "PracticeErrorMessage"}
fmt.Println(practiceError.Error())
}
出力結果です。設定したMessageの内容が表示されています。
ダックタイピング
Goのダックタイピングについては、次の記事に詳細がまとめられています。
エラーハンドリング
Goではtry,catchのような例外処理がありません。
その変わりに、エラーが起きるような関数・メソッドでは、返したい値に加えて、エラー型を返すようにします。
以下は、文字列の値を返す関数です。引数の文字列が空だった場合は、エラーを返すようにしてみました。
折角なので、上記で作ったPracticeErrorを活用することにしました。
// 与えられた文字列の長さを返す。返り値はintとerrorの2つ
func GetStringLength(str string) (int, error) {
if str == "" {
// 文字列が空だったら、errorにPracticeErorrのオブジェクトを代入して返す
return 0, PracticeError{Message: "String is empty"}
}
// 文字列が空でなければ、errorにはnilを代入して返す
return len(str), nil
}
では、関数を実行します。
package main
import (
"fmt"
)
type PracticeError struct {
Message string
}
func (e PracticeError) Error() string {
return "Error. Message : " + e.Message
}
// 与えられた文字列の長さを返す。返り値はintとerrorの2つ
func GetStringLength(str string) (int, error) {
if str == "" {
// 文字列が空だったら、errorにPracticeErorrのオブジェクトを代入して返す
return 0, PracticeError{Message: "String is empty"}
}
return len(str), nil
}
func main() {
emptyStr := "" // あえて空文字を用意
length, err := GetStringLength(emptyStr)
// エラーが発生していたら、エラーメッセージを表示する
if err != nil {
fmt.Println(err)
return
}
// エラーが発生していなかったら、文字列の長さを表示する
fmt.Printf("Length : %d", length)
}
空文字をGetStringLengthに渡したので、出力結果は以下のようになりました。
このように、エラーが置きているかどうかを、返り値がnilかどうかで判断することで、エラーハンドリングします。
感想
初めてGo言語を触ってみて、並行処理が簡単に実装できるのが良いと思いました。私自身、並行処理には苦手意識があり、C#やPythonでは使ってこなかったのですが、Goなら実装できそうです。Goで並行処理を勉強した後に、他の言語にも知識を展開できるようになりたいです。
インターフェースの実装は、日頃C#を触っている私には慣れが必要そうです。