新卒2年目がGo言語に挑戦してみた!

お久しぶりです!和尚です?

前回ブログを書いたのが入社すぐだったので、一年ぶりの登場です!
さっそくですが、新卒2年目になりました〜✌️

一年目はPHPとJavascriptをメインにサーバーサイドとWebフロントのお仕事をしていました!
直近では HONNE WebHONNE100万DLページ なんかを(まさかのデザインから)作ったりしてます?

エンジニアは1年1言語と言われますが(諸説あり)、丁度良く2年目に突入してすぐにGo言語のプロジェクトに参画することになったので、そこで培った知識と新米エンジニアなりの見解をブログに記していきたいと思います!

 

挑戦したものについて

 

なぜGo言語に挑戦しようと思ったのか

挑戦しようと思った理由については、前述の通りGo言語で書かれたプロジェクトに参画することになったのが主な理由というというのもあるのですが、気持ちとしては前から「Go言語は流行っている!」「需要あり!」という記事をよく目にしていたので、次やるならGo言語かなと頭の隅の方で考えていました。

とはいうもののフワーっとした何となくやってみたい。という気持ちだけでは人間なかなか一歩目が踏み出しにくい。

そんな時にタイミングよくGo言語で書かれたプロジェクトに参画させてもらえることになり、運良く一歩目を踏み出すことに成功しました!

何と言っても弊社に三人いるチーフエンジニアのうちの一人がGo言語に精通しており、その方が手がけてきたプロジェクトだったのでGo言語未経験者としてはかなり心強かったです?

チーフのブログ

Golangの旅(Let’s GO

参考サイト

年収順プログラミング言語ランキング&稼げるおすすめの言語2020

【2020年最新】プログラミング言語の人気度、将来性、年収、汎用性を徹底比較

 

開発前にやったこと

とはいうもののGo言語のイロハもしらない私は何から勉強しようか…

そんな時には初心に返ってみようということで、大学時代にお世話になったドットインストール先生のGo言語講座を受けて、まずは基本文法をざっくりと頭に入れました。基本的に新しい言語をやるときは最初ざっくりと文法覚えて、後はコーディングしながら知識を継ぎ足していくので開発前にやったことと言えば書き方を覚えることくらいです。

Go言語はスクリプト言語のPHPと違いコンパイラ言語なので実行前にコンパイルが必要なのですが、なんと親切にコンパイルと実行を同時に行ってくれる

go run main.go

というgo runコマンドのおかげで、スクリプト言語をメインに触ってる人がよくいうコンパイラ言語いちいち面倒だよねという点はそこまで気になりませんでした。

ちなみにGo言語は、Dockerなどエンジニアには馴染み深いものから、メルカリさんやクックパッドさんなどの有名なWebサービスなどでも使われてるみたいです。

 

PHPを1年触った新卒2年目から見たGo言語

一年以上PHPとJavaScriptをメインに開発してきた私が特徴的だなと思った点をいくつか挙げてみます。

クラスが存在なく、構造体が存在する

まず、PHPとJavaScript(ES2015以降)と大きく違う点としてはGo言語にはクラスやオブジェクトという概念がなく、代わりに構造体というものが存在します。

構造体はフィールドとメソッドを含んだ型のことで、これを使ってデータのまとまりを定義します。かなりざっくりですが、構造体は継承不可能なクラスだと思ってみると、わかりやすいかなと思います。

クラスが無い?継承が出来ない?構造体に構造体を埋め込む?など最初は混乱しましたが、オブジェクト指向言語のクラスとオブジェクトの関係性を構造体とインターフェースに置き換ることができるので、そこさえ脳内変換できれば然程難しいことでもありませんでした。

そして冒頭で一年に一つとか言いつつ、最近個人的にSwiftUIを使ってiOSアプリの開発を仕事終わりにしているのですが、SwiftUIでは構造体やポインタが頻出するので脳がGo言語の文法に慣れているうちはかなり開発が楽です(笑

もしiOSアプリとサーバーサイドの開発両方をやりたいというエンジニアさんがいたら、Go言語とSwiftUIを使ったアプリ開発をおすすめします。

ポインタ

PHPを長いこと触っていたのでポインタという存在に最初の方は脳が追いつきませんでした。大学時代にC言語を勉強したのでポインタ自体がわからないわけではないのですが、使ってこなかったというだけでやはり苦手意識があったので唐突に出てきて脳がプチパニック( ^ω^ ) 

今回の開発だとSQLlパッケージの中のSQL文を実行する関数の引数にポインタを渡すことで、その変数に値を入れてくれるようなものがありました。結構パッケージ関数の引数でポインタを要求されることがあるので、ポインタでつまづきそうという方は事前に勉強しておいたほうがよいかと思います。

型に厳しい

PHPやJavaScriptは型が緩く基本は1+”1”をしてもルールに乗っ取って計算してくれて怒られることはありません。(この場合は片方が文字列なので11になりますね)

引数の型宣言も戻り値の型宣言もしなくても問題なくコードを書くことが出来ます。

ただしGo言語は違います。

一応何でも入るinterface{}型というものが存在するのですが、格納したあとに計算などに使用する場合はInt型やfloat型などに戻す必要があります。ここが個人的には結構面倒でした。

型がわからなくなった場合は以下のどちらかで値を確認するこができます。

fmt.Printf("%T", value)

fmt.Println(reflect.TypeOf(value))

PHPもバージョン7から厳格な型チェックができるようになっており、引数や戻り値の型によるバグをGo言語同様事前に防ぐことができます。ただ、PHPの厳格モードって引数の型や戻り値の型書き忘れた場合は通常モードになっちゃうんですよね…そして書き忘れても何も怒られない…。

兎にも角にも、型でよく怒られるのを覚悟してしっかり書いていきましょう。

Switch文がスッキリ書ける!?

なんとGo言語だとSwitch文がスッキリ書けます!理由はGo言語はSwitch文にはbreakがないからです!!!(重要)

※ breakするまで連続で処理書きたいって方は、他の言語だとbreakを書かない場所に「fallthrough」を書けばできます。

条件によって異なる文字を出力するようなSwitch文を書いて比較してみます。

PHPの場合

switch ($language) {
    case "jp":
        echo "日本語";
        break;
    case "us":
        echo "英語";
        break;
    default:
        echo "不明";
}

Go言語の場合

switch language {
case "ja":
    fmt.println("日本語")
case "us":
    fmt.println("英語")
default:
    fmt.print("不明")
}

一目瞭然。かなりスッキリ書けますね。

なので私はPHPでswitchを使うときはbrackを書かないで続けて処理させたい場合やcase内でreturnしてbreakを書かなくても済む時だけでして、それ以外はifで対応していました。

Go言語は積極的に使いたくなるような作りをしています。

JSONの処理が面倒!?

豊富なパッケージが揃っていてシンプルに書けるGo言語もJSONの処理に関してはPHPに比べるとかなり複雑になってしまいます。

今回の開発でDBにJSON形式で保存しているデータがあったのですが、それの処理に苦労しました。

例えばこのようなJSON形式のデータがあったとします。

{
    "name": "bravesoft株式会社",
    "bravesrs": [
        {
            "id": 1,
            "name": "ブレイブ太郎",
            "age": 40
        },
        {
            "id": 2,
            "name": "和尚",
            "age": 24
        }
    ]
}

ここから社員の名前だけを取り出して連続して標準出力することにします。

PHPの場合

<?php

$json_str = '{"name": "bravesoft株式会社","bravers": [{"id": 1,"name": "ブレイブ太郎","age": 40},{"id": 2,"name": "和尚","age": 24}]}';

foreach (json_decode($json_str)->bravers as $braver) {
   echo $braver->name . "\n";
}

Go言語の場合

package main

import (
   "encoding/json"
   "fmt"
   "log"
)

type (
   Company struct {
       Name    string   `json:"name"`
       Bravers []Braver `json:"bravers"`
   }
   Braver struct {
       ID   int    `json:"id"`
       Name string `json:"name"`
       Age  int    `json:"age"`
   }
)

func main() {
   jsonStr := `{"name": "bravesoft株式会社","bravers": [{"id": 1,"name": "ブレイブ太郎","age": 40},{"id": 2,"name": "和尚","age": 24}]}`

   jsonBytes := ([]byte)(jsonStr)
   companyData := new(Company)
   if err := json.Unmarshal(jsonBytes, &companyData); err != nil {
       log.Fatal(err)
   }

   for _, braver := range companyData.Bravers {
       fmt.Println(braver.Name)
   }
}

PHPの方は若干強引な感じもしますが、それでもコードの長さが一目瞭然ですね。

Go言語の場合はJSONパッケージを使って、JSON文字列をbyte型配列に変換したものとJSON形式と構造になるようにした構造体型の変数のポインタをUnmarshal関数に渡すことで、変数の方に各JSONの値が入るようになっています。そこからfor rangeを使って名前を取り出して出力します。

一方PHPはjson_decode関数で一発でJSON文字列をオブジェクトに変換してforeachを使って名前を取り出して出力します。

データを使うまでの道のりがGo言語はPHPにくらべて長いのでストレスでした。ただJSONに合わせた構造体を作成しなければならないので不正な値を防ぐという点ではよいのかもしれません。

フレームワークの日本語資料が少ない

そして開発で地味に苦労した点ですが、今回使用したフレームワークEchoの日本語の参考サイトがかなり少なかったです。

公式ドキュメントもあまり詳しく書いているわけでもなかったので(読解力がないだけ?)、公式ドキュメント以外にも海外のエンジニアさんの技術ブログなどをGoogle翻訳にかけながら時々にらめっこしていました。

PHPは日本で使ってる方が多いのでドキュメントから参考資料まで日本語資料がとても豊富なので、その差はすごく感じました。

一番苦労した点は、echoのhtml template部分で公式ドキュメントに記述がほとんどなかったところです…

今後の自分のためにもいくつか記載しておきます。

//繰り返し処理
{{range $idx,$value := $List}}
<p>{{$idx}}:{{$value.Name}}</p>
{{end}}

//条件系
// if a == b
{{if eq a b}}
<p>aはbと等しい</p>
{{end}}

// if a != b
{{if ne a b}}
<p>aはbと等しくない</p>
{{end}}

// if a < b && c > d
{{if and (lt a b)(gt c d)}}
<p>aはb未満且つcはdより大きい</p>
{{end}}

// if a <= b || c >= d
{{if or (le ab)(ge c d)}}
<p>aはb以下またはcはd以上</p>
{{end}}

文字列を一定文字数以降を切り捨てるなど独自の関数を用いて変数を変えたりしたい場合は、templateパッケージに関数を追加して使用することができます。

他にも良いところがたくさん

他にもGo言語が特徴的なところしては

  • コンパイラ言語なので実行速度が早い(およそPHPの30倍)
  • 並行処理が簡単に書ける(関数orメソッド呼び出しの前にgoとつけるだけ)

などが挙げられます。

並行処理については残念ながら今回の開発ではうまく活用することは出来ませんでした(T ^ T)

強いていうなら、通知部分を並行処理で書いたくらいですかね?

Go言語はシンプルだけど奥が深いです…

 

他の自社サービスでもGo言語を用いてるプロジェクトがあるので、今回のプロジェクトに限らず出来ることが自ずと増えたのでよかったなと思っています。

次の投稿がいつになるかわかりませんが、次回お会いする時は更にグレードアップした和尚に期待です✨