Forkwell Press | フォークウェルプレス

SHARE

目次

目次

SHARE

キャリアとスキルアップ

「Go言語で楽しくなるシステム開発:基礎から実践テクニック」mattn × 渋川よしき

「Go言語で楽しくなるシステム開発:基礎から実践テクニック」mattn × 渋川よしき
この記事は、2023年3月8日発売 『Go言語プログラミングエッセンス』著者 mattn 氏と、『実用Go言語』著者 渋川 氏 による connpass勉強会 を再編集し、記事化したものです。

↓↓↓ Vol.1 はコチラから ↓↓↓

【Go言語入門】Go言語とは?

まずはGo言語の基礎をおさらいしていきましょう。

Go言語は、Googleと世界的エンジニアが共同開発した言語

Go言語は、Googleによってプログラミング環境を改善する目的で開発されました。この開発には、C言語を設計した世界的なエンジニアが携わっています。

Go言語の開発者

  • Kenneth Lane Thompson (Ken Thompson)…… UNIX開発者  / C言語 発案者
  • Robert C Pike(Rob Pike)…… UNIX開発者  / UTF-8 発案者
  • Robert Griesemer …… 高速 JavaScript エンジン V8 開発者

「Explicit is better than implicit」これは開発者 Rob Pike 氏が好む言葉で「曖昧さよりも厳密さの方が良いよね」という意味。彼が創り上げた Go言語の精神を象徴しています。

Go言語は世界中の企業で使われている

Google、PayPal、American Express、DropBox、Cloudflare、Facebook、Microsoft、Netflix、Salesforce、Twitter、Uber、Twitch、Dailymotion、SendGrid、SoundCloud、Cookpad、CyberAgent、サイボウズ、DeNA、はてな、LINE、メルカリ、楽天、さくらインターネット、Yahoo! JAPAN、ZOZO、ぐるなび、eureka …… 引用:https://github.com/golang/go/wiki/GoUsers

Go言語が活躍する領域

コマンドラインプログラム / ウェブ API サーバ / バックエンドジョブ / 広告配信システム / ハードウェア制御 / 機械学習

Go言語の特徴

● 静的型付コンパイル型言語

○ ソースコード上に型を明記し代入の束縛を行う

○ コンパイルが通らないと実行できない

● スタティックバイナリを生成する

○ 依存ライブラリが少なくポータブルなバイナリを生成できる

● C言語のような書き味でありながら GC がある

○ ポインタやアドレス参照を使う

○ メモリの開放は自動で行う

● 非同期を簡単に扱えるランタイム

○ goroutine で並行処理し channel による通信を行う

C言語しかなかった時代の人にとっては夢のような言語に感じるでしょう。ポインタやアドレス参照を使うので敷居の高さは感じるかも知れませんが、比較的、理解しやすい言語です。

Go言語が優先したこと

● 読みやすさ声に出して読める程の読みやすさ

● 学びやすさ

● 開発者が明示的に処理を中断させるメンタルモデル

● 複雑にしないために予約語を増やさない

※ 現時点での判断であり良い方法があれ改良される事もある

Go言語が目指していること

● なるべく曖昧さを与えない

● 明示的に開発者がコードで意思決定するプログラミング

厳密さや簡潔さをもたらすので、本来はシェルで実装を済ませるような処理を、あえて Goで実装する人も多いのです。私も同じ。失敗経験はほとんどありません。

Go言語の難易度

2013年にGoが実施した「あなたの課題はなんですか?」というサーベイの結果は、エラーハンドリング / スタックトレース が1位。

引用:https://go.dev/blog/survey2023-q1-results

Go言語は Javaのように例外を扱いませんし、 Rust のようにリザルト型言語ではありません。関数の戻り値にエラーがあれば対処が必要です。

バグを生み出さないコツを紹介

前述のようにエラーの取り扱いが難しいとされるGo言語ですが、バグを減らすコツはあります。

  • 全てのエラーを明示的に対処し暗黙をなくす
  • エラーを受け取ったら、処理を中断し呼び出し元へ返す
    • 必然的に early return になる

よくあるコードサンプルを例に見てみましょう。

このように、defer を上手く使い、起こるエラーを全て呼び出し元へ返すことで、未想定のバグを減らす事ができるのです。

Go言語のメリット

非同期プログラミングが簡単

個人でも高性能なサーバーを保有する時代。そのリソースを最大限に活用するためには非同期プログラミングが有効です。少し難しいイメージがあるかも知れませんが、2つの方法によって非同期プログラミングを簡単に実現できます。

goroutine / channel

● goroutine

非同期にしたい関数に go を付けるだけで簡単に非同期を呼び出せる

● channel

非同期で実行される goroutine の間で安全に通信する方法

コンテキストと組み合わせると、さらに便利に

一方、goroutine や channel は便利だけど、エラーハンドリングが難しいという声も耳にします。goroutine や channel だけで処理するよりも、コンテキスト(goroutine の呼び出し元が goroutine を安全に終了するための機能)と組み合わせて使用することをおすすめしています。コンテキストを適切に使用することで、goroutine が放置されるリスクを避けることができます。

【まとめ】悩みの種が減って、システム開発が楽しくなる!

Go言語は…

● 静的型付のコンパイル型プログラミング言語

● スタティックバイナリが吐かれる(デプロイが楽)

● goroutine/channel により簡単に非同期プログラミングができる

● スケールするのでクラウド向き(クラウドでのシステム開発向き)

簡単非同期プログラミングでシステム開発が楽しくなる!!

「Go 1.21」でできること

1.20でプレビュー搭載されたPGO(Profile-guided optimization)がGA(generally available)に到達しており、default.pgoファイルによる最適化でプログラムのパフォーマンスが向上。ホストアーキテクチャにより異なるがGoプログラムの2-4%の高速化が見込まれる。組み込み関数(The built-in functions)に引数の最大値、最小値を返すmin/maxやパッケージにslicesやmaps、ログを取り扱うlog/slogなども加わる。引用:https://news.mynavi.jp/techplus/article/20230622-2710414/

下記の動画で各機能を詳しく解説しています。

min/max / clear / loopvar / pgo / slog / maps / slices / wasi

Slidoに届いた視聴者からの12Qs

Q:Go言語に適したプロダクト

mattn:Go は I/Oバウンド(ネットワークやディスクへの入出力などがボトルネックになる状況)が多く発生することが予想される場合に適しています。CPUバウンド(計算処理が主体となるフィボナッチ数列のような複雑な計算を多く行う処理)には不向きです。

渋川: I/Oバウンド っていうのは、たとえばWebサーバーのようにネットワークアクセスが多い、あるいはリクエストを投げる側、またはデータベースやファイルの読み書きなどを指しますね。

Q:「Go 1.18」から導入されたジェネリクスの扱い

mattn:「Go言語にジェネリクスが追加されてからしばらく経ちましたが、コミュニティではどういう扱いなのでしょうか?」という質問ですね。Goって新しいバージョンになると、古いアーキテクチャをどんどん切っていくので、古いGoをサポートしたい方にとってジェネリクスはまだ受け入れられていない様子です。私もメインでは使用していないです。

渋川: Goのポリシーだと最新の2バージョンはサポート対象ですよね。現状だと、1.19、1.20がサポート対象なのでアクティブなバージョンはすべてジェネリクスをサポートしていると言えます。それよりも古いバージョンをサポートしたい方もいるんですね。

mattn:そうですね。

― ジェネリクスの使いどころはありますか?

mattn:ジェネリクスを活用すると、コード内で演算を行う際に型パラメータを使用できます。例えば、プラス演算子を使うと、文字列であれ数値であれ出力可能です。any型ではなく comparable型を用いることで、数値を入力したいのに間違って文字列が入力される問題を防げます。

ただし、Goのジェネリクスには一定の制約があります。例えば、可変長の型パラメータは許容されていません。複雑な処理をしようとすると問題が発生する可能性があります。その制約を理解して、正しく使用することが重要です。

Q:channelのバッファの数値を決める基準

mattn:バッファ数は処理の性質によります。まずはバッファなしで実装し、必要に応じてバッファ数を微調整するのが適切です。バッファを過剰に設定すると、本来ブロッキングして欲しい箇所で逆にバグを引き起こす可能性があります。

渋川: あとはCPUのコア数やタスクによっても変わってきますよね。

Q:シェルのタスクをGoで書くメリット

mattn:シェルは環境変数が未定義だと書きにくいのですが、Go はより明示的に書けます。値を取得したら直ちにチェックするという Go の原則に従うと、コードが簡単に書けるかと思います。

渋川: Windows環境で頻発に起こる、文字コード(シフトJISかUTF-8か)のトラブルも、Goだと大幅に軽減できますよね。環境変数が空のせいで想定外のフォルダを削除してしまうとかも防げそうです。(注釈: rm $WORK/libという命令は、$WORKが定義されていない場合にルートの/libを削除しようとする)

mattn:そうですね。型が明確な Go だと、本来数値で処理すべき部分が文字列のままになるといったバグが少なくなる点もメリットですね。

Q:勉強用のコードリーディングにおすすめのOSSは?

mattn:お手本にするなら、やはり標準ライブラリもしくは標準ライブラリの開発に携わる方が別で作ったOSSを参照するのがベストです。

渋川: 「クリーンアーキテクチャ」とか「コーディングスタンダード」で検索して出てくるものはあまりおすすめしません。Rob Pikeも「これはGoにおける標準ではない」とコメントを残しています。僕も Go標準ライブラリ、Go自体のソースをおすすめします。

Q:GCによるCPU使用率の増加を軽減する方法

mattn:「Goで作成されたプログラムが長期間にわたり大量のメモリを使用する場合(Go が世代別GC を採用していないせいか)CPU使用率が急激に増加します。軽減する方法はありますか?」という質問ですね。ここはGo自身も長年課題に感じている部分のようで、実は各バージョンアップごとに少しずつ改善されてきているんですよね。今回のバージョンから GC を実行するタイミングが固定ではなくなっています。

渋川: なるべく新しいものを使いましょうと。

mattn:そうですね。

渋川: 大量にメモリを取って大量に返すみたいな行動しちゃうと、Goに限らずどの言語でも、OSとのやり取りに時間を取られるので、sync.Pool を使用して Go 内で使い回すようにするとかですかね。あとは、1.20でExperimentalでリリースされたメモリアリーナもあります。小さいメモリをたくさん確保するXMLは遅いというissueが立っていますし、それを使うExcelパースに関しては僕ももうちょっと改善してほしいと思っています。

Q:無料でデプロイできるおすすめのサービスは?

mattn:「Go言語で開発したプロジェクトを無料でデプロイできるおすすめのサービスはありますか?(気に入っていた Heroku が有料化されてしまい…有料化されても一番コスパが良いのかしら?)」という質問ですね。もちろん、AWS、Oracle Cloud、GCPなど標準的なプラットフォームで動作する方が良いと思いますが、無料であれば、Fly.io ですかね。完全に現時点での僕の好みになってしまうのですが、コンテナを推奨できる点が良いかと。Vercel もアップロードは可能ですが、実行時にコンパイルが行われるようで、少し動作が遅いんですよね。

渋川: Go は起動時間が短いので、AWS Lambda や Google Cloud Run のようなサーバーレスのサービスを使うと、リクエストごとに処理を行うことが可能なので、かなり実用的だと思います。

Q:Goのロギングライブラリ zap or slog ?

mattn:「Go のロギングライブラリ zap は高パフォーマンスを売りにしていますが、高パフォーマンスは必要ですか?また、slog を使う際にも高パフォーマンスを重視すべきですか?」という質問ですね。ロギングを導入することで逆にシステムが遅くなることはよくあるんです。Go は速さより下位互換性を重視しているので、今後も互換性を保ちながら改良されていくと思います。slog に追加された Go1 は恐らく今後も壊れませんが、zap は将来的に古いインターフェースになり、コンパイルできなくなる可能性があります。どちらに投資するかはユーザー次第ですが、僕は slog に賭けたいなと。

渋川: 僕も slog 派。パフォーマンスが気になる場合は両方のライブラリをベンチマークしてみると良いですね。

Q:Go言語における channel の適切な閉じ方

mattn:「close済み channel に write すると panic になる仕様に悩みます。reader/writerの良い設計はありますか?」という質問ですね。goroutine の外部から channel を閉じると、実行時エラー(パニック)が起こる可能性があります。goroutine の内部で書き込み操作してみてください。要は、 channel を閉じる場所と書き込む場所が違うと、デッドロック(複数の処理が先に進んで消えてしまう状態)が発生しやすく、それがパニックを引き起こします。

渋川: 水道工事と同じで、なるべく上流を止めて制御するほうが良いですね。(下流側から)何かを閉じたいときは、データを書き込む側(ライター)に閉じたい意思を何かしらの方法で伝えて、書き込む側に channel を閉じさせるのが良いです。要はライターに全て集約すると混乱が少ないですね。

Q:Go言語のおすすめフレームワークは?

渋川: 特定のフレームワークに肩入れしないmattnさんの教義に反するかも知れませんが、いかがでしょう?

mattn:そうですね、私は一通り試してベンチマークするものの、仕事では Echo をよく使います。

渋川: Echo は人気ですよね。

mattn:何が良いかって企業が作ってるのでドキュメントがしっかりしているし、当分は壊れないだろうという安心感ですね。

渋川: 大きなものだと Echo が良いですよね。

Q:Go言語をどのように習得しましたか?

mattn:Go 初期から興味を持っていました。初期は「まともに動かない Windows を動かしたい!」という思いで、コントリビュートしてきました。その流れで、標準ライブラリのコードを読み漁りましたが、結局は標準ライブラリのソースが一番勉強になると思います。

渋川: 言語開発者自身の書くコードが、一番 Go を生かすコードになりますよね。

Q:Go言語に向いていないエンジニアは?

mattn:他の言語のルールを抜け出せない方。Java を意識してGoの良さを活かせず、汚いコードになってしまう人が多いです。Goはフラットに書いた方が綺麗に書けるので。向いてないというよりもったいないと感じることはあります。

渋川: 最近はJavaでも、あまり深くしないのが主流になってきているようですね。

Q:Go言語を使っていて楽しい瞬間は?

渋川: channel の使い方、非同期が決まると気持ちいいし楽しいですよ。 channel 周りの並列のテクニックは面白いですよ。

アーカイブ動画はこちら

ForlkwellPress ロゴ画像

フォークウェルプレス編集部

Follow

記事一覧へ

本サイト掲載の全て記事は、フォークウェル編集部が監修しています。編集部では、企画・執筆・編集・入稿の全工程をチェックしています。

Google Developers Expert Go

ソフトウェアハウスやSIerでソフトウェア開発に携わる。vi派生のテキストエディタVimの日本語化やプラグイン、Go言語などでOSS(オープンソースソフトウェア)の開発・コミュニティ運営に参加し、2019年からGoogle Developers Expert。 『みんなのGo言語』(2016年、2019年に改訂2版、技術評論社、共著)、『Go言語プログラミングエッセンス』(2023年、技術評論社、単著)を執筆。

渋川 よしき
フューチャーアーキテクト株式会社

シニアアーキテクト

自動車会社、ソーシャルゲームの会社の後、現職。実用Go言語、Real World HTTP、Goならわかるシステムプログラミングの執筆、エキスパートPythonプログラミングの翻訳などを行う。