モバイルエンジニアとして、CBcloudに参画したbigbackboomです。メインでAndroidアプリの開発を行なっています。今回Adventカレンダー二度目の投稿です。
弊社では全く使用されていないGoですが、私が前社でGoのサーバーを設計開発をしたので、私自身はGoをかなりの推し言語としています。今回は、Goの良いとこ、悪いとこを書いていこうと思います!
go言語って?
Goはオープンソースのマルチプラットフォームで動くコンパイル型言語です。言語仕様に、型安全、信頼、効率の三つに主眼をおいた言語です。
元々は2007年にGoogleに所属していた、ロバート・グリーズマー、ロブ・パイク、ケン・トンプソンが考案し、2009年に正式に発表されました。彼らは良いプログラミング言語とは以下のように定義しました。
- 簡潔かつリーダブルな言語
- 静的型付
- webpageなどでの軽量差
- 速い
- スケールのしやすさ
- ツールの有無関係なくかける事、ただしツールのサポートもする事(IDE等)
- ネットワークプログラミングと、並列処理に優れる事
その問題を解決したのが、go/golangというわけです。
実際に構文を見てみよう
初めてのgo
以下のようなmain.goと名前をつけたファイルに以下のように記述します。
package main
import "fmt"
func main() {
fmt.Print("hello world")
}
ターミナルから、以下のコマンドを入力
$ go run main.go # hello world
これだけです。ちなみにバイナリを作りたい場合はgo build main.go
とやるとバイナリーが作成されます。単純に試しに動かしたいだけなら、runで十分ですね。
変数
基本型
var 変数名 型 = 初期値
省略型
var 変数名 = 初期値
省略型2
変数名 := 初期値
例
// 基本型
var hoge string = "hello world"
// 省略型
var hoge = "hello world"
// 省略型2
hoge := "hello world"
注意: goでは使っていない変数があるとコンパイルエラーになります*
特にルールはないですが、私はスコープが短い場合は省略型2、長い場合は省略型を使います。
他にも例では、hoge
を変数名で使っていますが、goはスコープがすごく長くない限りは、変数名は短ければ短いほど良いルールになっています。よく見るのが頭文字だけ取って、apple
ならa
とだけ命名したり,hogeHoge
なら'hh'としたりです。三文字以上になる場合は、str
のようにワード全部が類推できる省略の仕方をします。できない場合は、普通に変数名を書いたほうがいいと思います。
構造体
構造体の作成
type Hoge struct {
id int
name string
}
使用例
type Hoge struct {
id int
name string
}
func main() {
a := hoge { 1, "bigbackboom" }
fmt.Print(a)
}
実は・・・
厳密には構造体は上のような定義をしなくても使えるんです。型として定義しなくていいのであれば以下のようにできます。
func main() {
test := struct {
id int
name string
}{
1,
"name",
}
fmt.Print(a)
}
実は構造体を汎用的な型として使うのに必要なのは、typeです
typeとは?
c言語と戯れたことがある方は分かると思いますが、エイリアスを作成する予約語です。つまり、構造体を作成するためのおまじないの一部ではありません
他の使い方としては、こういうこともできます。
type Password string
func main() {
p := Password("hogehogefugafuga")
fmt.Print(p)
}
これは、string型をPassword名称で使うと定義しています。 関数の引数などで、コンテキストをはっきりさせたい時などに使用できます。
関数
基本
func 関数名 (引数 型) {
// do something
}
戻り値あり
func 関数名 (引数 型) 戻り値 {
// do something
}
複数戻り値
func 関数名 (引数 型) (戻り値型, ..., 戻り値型) {
// do something
}
例
// 基本
func hoge(fuga string){
fmt.Print(fuga)
}
// 戻り値あり
func hoge(fuga string) int{
fmt.Print(fuga)
return 0
}
// 複数戻り値
func hoge(fuga string) (int, error) {
fmt.Print(fuga)
return 0, nil
}
goの特殊なところの一つは、複数の戻り値に対応しているところです。try/catchが存在しないので、goではエラーを受け取る際は、二つ目の戻り値がnilかそうでないかでエラーかどうかを判定することが多いです。
func hoge(fuga string) (int, error) {
fmt.Print(fuga)
return 0, nil
}
func main() {
p, err := hoge("hello world")
if(err != nil){
// ここきたらエラー
return
}
fmt.Print(p)
}
goっぽい書き方を意識するときは、エラーの有無を第二戻り値に設定するだけで、グー!!と高まります。
interface
interfaceの話をする前に一つ大事なことを言わなければなりません。にgoはクラスが存在しません。ただし、構造体に関数をはやして、構造体変数の値を更新することができます
構造体の関数
以下のように書くことで、構造体に関数をはやすことができます。fugaは構造体を初期化しないと、呼ぶことはできません。
type Hoge struct {
id int
name string
}
// レシーバー
func(h Hoge) fuga(a string) {
fmt.Print(h.name)
}
func main() {
p := Hoge{1, "big"}
p.fuga("haha") // big
}
構造体のポインターレシーバー関数
しかしながら、上記では残念ながら構造体の値を更新することはできません。なぜならh
は呼び元の構造体のコピーでしかないからです。レシーバーをポインタレシーバーにすることで、参照を受け取り値を更新することができます。
type Hoge struct {
id int
name string
}
// 通常レシーバー
func(h Hoge) fuga(a string) {
h.name = a
}
// ポインターレシーバー
func(h *Hoge) fugaPtr(a string) {
fmt.Print(h.name)
h.name = a
}
func main() {
p := Hoge{1, "big"}
p.fuga("change!")
fmt.Print(p.name) // big
p.fugaPtr("change!")
fmt.Print(p.name) // change!
}
interfaceの実装
構造体はinterfaceで定義されている関数を実装することで、暗黙的にinterfaceの実装を行うことができます。Rubyなどで有名なダックタイピングですね。 ただし、型のないRubyと違い、特定の関数を呼ぶ際は必ず、キャストする必要があるので注意が必要です。ここら辺は、逆に型あり言語の継承の概念と同じですね。
type Fuga interface {
printMessage(string)
}
type Hoge struct {
id int
name string
}
func(h Hoge) printMessage(a string){
fmt.Print(a)
}
func testFunction(f Fuga){
f.printMessage("bigbackboom")
}
func main() {
p := Hoge{1, "big"}
testFunction(p) // bigbackboom
}
testFunction
はFuga
interfaceを引数にしていますが、Hoge
はFuga
が定義するprintMessage
を実装しているので引数として受け入れることが可能です!
まとめ
ここまでみたら分かると思いますが、Goは昨今の流行りのスクリプト言語と比べるとかなり変わった構文になっています。さらにいうと、ライブラリなどもかなり貧弱なので、Rubyに慣れているとこの言語は使いやすいのか?と疑問を浮かべてしまうかもしれません。
ただ、サーバーで使えば、オーバーヘッドが圧倒的に少ないのと、シンプルなので、「最低限の機能」を実装する場合は十分活躍すると思います。そのため、マイクロサービスのAPIで採用されるのかもしれませんね。
皆さんもぜひ、コンパイルするところから初めてみてください!