【Go】VSCodeでGo言語の開発環境を整えてみよう【Golang】【主にWindows・初心者向け】

#### Visual Studio Code(VSCode)でGoによる開発しよう

　クラウドやコンテナ界隈でもよく聞くGo言語。最近入門したので備忘録を兼ねて、このエントリではVSCodeを使ったGo開発環境構築の手順を記してみます。  
　以下、主にWindowsを想定していますが他のOSでもそれほど変わりませんので、適宜読み替えてください。





余談ですがGo公式のブログには[Go's New Brand](https://go.dev/blog/go-brand)記載のカラーや[Go fonts](https://go.dev/blog/go-fonts)記載の公式Goフォントなどもあり、スライド資料作成や画像作成などに使えます。ロゴやマスコットのゴーファー君も透過PNGの画像が用意されていてありがたいです。


#### Go言語のインストール手順

　ネット上でも各所に載っていますが、この記事でもまとめてみます。

https://go.dev/

- 公式サイトの[Downloads](https://go.dev/dl/)ページから、Windws/Mac/Linux用それぞれのバイナリファイルをダウンロード。
- **Windows:** `.msi`のインストーラー形式なのでこれを実行。
    - インストール先がデフォルトは`C:\Go\`になっているので適宜変更。
    - [Install]ボタンでインストール実施。
    - Windowsシステム環境変数の`Path`にインストール先の  
    `{Goのインストール先}¥bin`  
    を追加。これでコマンドプロンプトからGoのコマンドが実行できるようになる。
    - Windowsの環境変数`GOPATH`に任意のフォルダを設定する。ホームの`%USERPROFILE%¥go` がオススメ。実際の値は  
    `C:\Users\<username>\go`  
    のようになる。ここにGoのライブラリが入るので重要。
    - ここでフォルダ名は厳密には`go`でなくともよいのだが、他のアプリケーションや他の用途のファイルが混在しないように注意。基本`go`にしておく。
- **Mac:** `.pkg`ファイルをダウンロードしてインストール。
    - インストール先は `/usr/local/go` になる。
- **Linux:** `.tar.gz`ファイルを解凍して配置。
    - インストール先はMacと同様
    - 環境変数`PATH`に`{Goのインストール先}/bin`を設定。
    - 環境変数`GOPATH`に`$HOME/go`を設定。


　コマンドプロンプトやターミナルから `go version` と打って結果が返ってくればインストール成功です。

```shell
CMD> go version
go version go1.18 windows/amd64
```

付属のGoツールで`go env`と打つとGoビルドシステムに関する環境変数一覧をずらーっと表示してくれます。このうち、開発者に関係のある`GOROOT`と`GOPATH`が正しく設定されているのを確認します。

```shell
CMD> go env GOROOT
C:\go1.18 (のような値)
CMD> go env GOPATH
C:\Users\<username>\go (のような値)
```



#### VSCodeでの開発環境準備

　Go言語のプログラムの実体は拡張子`.go`のテキストファイル＋αなので、基本は何を使っても書けます。vimやemacsなどでガシガシ書いている強者の方も割とおられますね。往年のEclipseやIntelliJ IDEAにもGo用のプラグインがあります。  
　Go専用のIDEでは、JETBRAIN社の一連のIDEシリーズの中にGo専用のGoLandというIDEがあり、補完もかなり強力なようです。(ただし例によって有償...)

https://www.jetbrains.com/ja-jp/go/

　OS間の差をほぼ気にせず開発できるGoとしては、クロスプラットフォームな開発ツールを使うのも良いでしょう。というわけで本エントリではモダンな開発の超定番、VSCodeを使うことにします。ネット上の情報を見てもだいたい皆さんVSCodeを使っている方が多いようです。



##### 拡張機能『Go』を入れる

　拡張機能の検索窓で"go"と打つと出てくる、言語の名前と同じそのまんまの『Go』が公式の拡張機能です。これをインストールしてVSCodeを再起動します。



この拡張機能『Go』は元はVSCode自体の開発チームがメンテしていましたが、2020年にGoチームに担当が移ったそうで力が入っているのが分かります。またこの記事によると、Go開発者の41%がVSCodeを使っているとあります。

https://atmarkit.itmedia.co.jp/ait/articles/2006/11/news118.html

　この拡張機能『Go』の実体はGitHub上にあり、リポジトリ名はvscode-goとなっています。

https://github.com/Microsoft/vscode-go


　インストール後、VSCodeで後WindowsならCtrl + Shift + p でコマンドパレットを開き、`"gotools"`などと入力すると以下のコマンドが候補に出ます。

```shell
> Go:Install/Update Tools
```
しばらく待つと一緒にインストールできる標準のツール類が一覧表示されます。Goのバージョンやインストールした時期によって違うようですが、実際にやったときは以下の8つが出ました。

```shell
  go-outline  (JSON表現を抽出するためのシンプルなユーティリティ)
  gotests  (特定のソースファイルまたはディレクトリ全体のテスト生成)
  gomodifytags  (?)
  impl  (インターフェースを実装するためのメソッドスタブ生成)
  goplay (Webインターフェース)
  dlv (Goのデバッガ)
  staticcheck (静的チェッカー)
  gopls (Go言語用のLanguage serverのデファクト)
```

表示されたものはすべてインストールすべき基本ツール類ですので、全てチェックしてOKで実行します。ネットワークアクセスが走ってインストールが行われます。

##### ネットワーク問題でツールのインストールが失敗する場合

　社内イントラネットからプロキシ経由でWebに繋いでいる場合などで、上記ツールのインストールが失敗することがあります。VSCodeのコンソール窓を調べていくと以下のように表示されます。

```shell
Installing github.com/ramya-rao-a/go-outline@latest FAILED
Installing github.com/cweill/gotests/gotests@latest FAILED
Installing github.com/fatih/gomodifytags@latest FAILED
Installing github.com/josharian/impl@latest FAILED
Installing github.com/haya14busa/goplay/cmd/goplay@latest FAILED
Installing github.com/go-delve/delve/cmd/dlv@latest FAILED
Installing honnef.co/go/tools/cmd/staticcheck@latest FAILED
Installing golang.org/x/tools/gopls@latest FAILED
```

　僕の場合もこれが起こり、GitHubや別のGitリポジトリには繋がるしVSCodeの拡張機能は繋がるしなんでだろう...としばらくハマりました。  
　解決方法の一つは、コマンドプロンプトでプロキシの環境設定を正しく設定した後、Go言語付属のコマンド **`go install {リポジトリのFQDN}/{リポジトリのパス}`** で直接インストールすることでした。

```shell
CMD> SET http_proxy=http[s]://{プロキシサーバーのホスト}:{プロキシサーバーのポート}
CMD> SET https_proxy=%http_proxy%
```

上記はWindowsの場合で、Linuxなら `export http_proxy=...` のようになります。場合によっては`{ユーザー}:{パスワード}@`をホストの前に付けるケースもあります。  
その後同じコマンドプロンプト上で `go install {パッケージのURL}` を順番に叩いていくことでインストールできました。

```shell
CMD> go install github.com/ramya-rao-a/go-outline@latest
CMD> go install github.com/cweill/gotests/gotests@latest
CMD> go install github.com/fatih/gomodifytags@latest
CMD> go install github.com/josharian/impl@latest
CMD> go install github.com/haya14busa/goplay/cmd/goplay@latest
CMD> go install github.com/go-delve/delve/cmd/dlv@latest
CMD> go install honnef.co/go/tools/cmd/staticcheck@latest
CMD> go install golang.org/x/tools/gopls@latest
```

　VSCode上で`Go:Install/Update Tools`を実行すると内部で上のgo installコマンドを呼び出していたので、Goコマンドがプロキシを経由して実行できるようにしてあげる必要があったわけですね。

　現在のGo言語付属のコマンドでは、`go install`でこうしたパッケージ類をインストールすることになっています。ちょっと前までは`go get`でやっていたので、ネット上の少し前の情報だとそう書かれていることもあります。ご注意ください。  
　**2021年2月**の[Go1.16](https://tip.golang.org/doc/go1.16)から、新規バイナリのインストールには`go install`を使うようになっています。  
　このエントリを書いている時点で`go get -v -u {パッケージのURL}` などを叩くと、以下のように今はgo install を使うのでござると英文で教えてくれます。

```shell
go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.
```

##### ツール類の確認方法

　VSCodeのコマンドパレット経由/コマンドプロンプト経由でツール類がインストールされると、環境変数`GOPATH`で指定されたフォルダ配下に実物がインストールされます。更新日が新しくなっていることなどで確認できます。

```shell
C:/Users/<username>/go
  bin/
    // 今回の例だと、合計8つのツールの実行ファイル.exeが新たに配置
    dlv.exe
    ...
  pkg/
    // ツールのパッケージの格納場所
  src/
    // ユーザが定義したプロジェクトのソース置き場と標準でなっている場所
```

　これでめでたくVSCode上で拡張子`.go`のファイルを編集するとGoのソースファイルと認識され、コード補完やフォーマッティングが動くようになります。


#### VSCodeの設定ファイルsettigs.jsonの設定


##### 言語個別の設定

VSCodeの歯車マーク-[設定] の後の右上の小さなアイコン `[設定(JSON)を開く]` クリックで直接設定できる`settings.json`。複数言語でVSCodeを活用し、フォーマッティングの拡張機能Prettierを使っている方は、この`settings.json`ファイルの第一階層に言語ごとの設定を書いているのではと思います。

```json
{
    // 中略...
    "[javascript]": {
        "editor.tabSize": 2
        "editor.insertSpaces": true,
        "editor.formatOnSave": false, // 重いときはコマンド呼び出しでフォーマット
        "editor.defaultFormatter": "esbnb.prettier-vscode"
    },
    "[typescript]": {
        "editor.tabSize": 2
        "editor.insertSpaces": true,
        "editor.formatOnSave": true, // ファイル保存時にPrettierでフォーマット
        "editor.defaultFormatter": "esbnb.prettier-vscode"
    },
}
```

ここと同じ並びにGo言語用の設定も追加しておきましょう。

```json
    "[go]": {
        "editor.tabSize": 2, // 公式のサイズは不明。2か4
        "editor.insertSpaces": false, // Go公式はスペースでなくタブ
        "editor.formatOnSave": true, // ファイル保存時にフォーマット
        "editor.defaultFormatter": "golang.go" // 変える
    },
```

　以下、それぞれの設定項目の解説です。

**`"editor.tabSize":`**  
　Go言語公式のインデント桁数は明記されていないようです。手近な情報で確認してみました。

**2桁：**

- 入門本の定番『スターティングGo言語』
- Webアプリケーション開発を扱った『Goプログラミング実践入門』

**4桁：**

- 最初の一歩で有名な [A Tour of Go](https://go-tour-jp.appspot.com/)
- [Effective Go](https://go.dev/doc/effective_go)など、Go言語公式[ドキュメンテーション](https://go.dev/doc/) ※中には4桁でない場合も混じっているような？
- [golang/go CodeReviewComments 日本語翻訳](https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d)
- Go言語のバイブルと呼ばれる『プログラミング言語Go』


　なお商業本『改訂2版 みんなのGo言語』の中は章によって混在でした。

　サーバーサイドのプログラミング言語ではRubyやScalaを除くとだいたい4桁が伝統でした。でもモダンな今どきの開発では2桁がもう主流だし2桁かな...？と思いきや、意外と4桁としているところも多いですね。まあここはお好みで。



**`"editor.insertSpaces":`**  
　僕も最初に知ったとき割と驚いたのですが、Go公式のインデントの指定はスペースではなく「タブ」なんですね。なのでここはfalse固定です。


**`"editor.formatOnSave":`**  
　trueにしておくと、ソースファイルを保存した瞬間にGo付属コマンドの`go fmt`を打った時と同様に公式で決まっている形式にフォーマットしてくれます。書き方に悩まなくて済むので楽です。宗教戦争を合理的に回避しているGoらしい思想です。  
　ソースファイルが長くなったり他のファイル数が多くなったりでだんだん動作が遅くなってきたら、falseにしておくこともできます。この場合はVSCodeのコマンドパレットで`"format"`などと打つと出てくる`ドキュメントのフォーマット(Format Document)`コマンドで同じことが実行できます。このへんの動きは他の言語と同じです。


**`"editor.defaultFormatter":`**  
　デフォルトのフォーマッターはフロントエンド開発だと拡張機能Prettierでおなじみの`"esbnb.prettier-vscode"`にしていることが多いかと思います。

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

フロントエンド界隈の技術がターゲットなのでPrettierにはGoのフォーマットはないんですね。この状態だとVSCodeが警告を出してくるので、上の設定例のように固定値でGo公式の`"golang.go"`を指定します。

　このへんは仕事など本格的な開発でプロジェクトやプロダクトや開発チームの固有ルールがある場合は、そちらに従ってください。


##### indent-rainbowの設定

　またインデントを色分け表示してくれて楽しい拡張機能indent-rainbowをお使いの方は、`editor.tabSize`と同じ値を設定しておくとGoコードのインデントも虹色になってくれます。

```json
    "indentRainbow.indentSetter": {
        "[javascript]": 2,
        "[typescript]": 2,
        "[ruby]": 2,
        "[php]": 4,
        "[python]": 4,
        "[go]": 2, // ここにeditor.tabSizeと同じ値を追加
    },
```



#### その他のGo向けVSCode拡張機能

　他にGo開発でほぼ必須になるようなレベルの個別のVSCode拡張機能は、特には見当たりませんでした。Go言語自体が備えている充実した標準パッケージやコマンド類、上の拡張機能『Go』で一緒に入るものでだいたい間に合うということでしょうか。さすがに開発環境回りはコンパクトにまとめていますね。  
　Go言語は開発環境をVSCodeに限定しているわけではないので、VSCodeの拡張機能ではなくGo自体のパッケージとして様々なツール類を展開しているというのもあります。コマンドで実行できる系のツールもいろいろありますね。

　Go言語に限定しない、VSCodeのおすすめ拡張機能はネットでも多数見つかります。  
筆者のはてなブログではVSCodeでPHP開発環境を整える記事で一覧を挙げていますので、こちらもどうぞ。

https://iwasiman.hatenablog.com/entry/20200727-php-on-vscode



#### 新規プロジェクトの作成方法


##### 別パッケージ内が呼べない場合がある！？

```go
udemy-gogo-golang
  sectionXXX/
    foo/
      bar.go // Bar関数が定義されている
    main.go
```

```go
main.goの中身
package main //sectionXX
import (
	"fmt"
	"foo"
)

func main() {
	fmt.Println(foo.Bar())
}
```

　任意の場所に作ったプロジェクトで、上のようにmain関数から別パッケージ内を呼ぼうとするとエラーが起こりました。

```shell
"foo"
could not import foo (cannot find package "foo" in any of
C:\go1.18\src\foo (from $GOROOT)
C:\Users\<username>\go\src\foo (from $GOPATH))/
"./foo"
 main.go:7:2: "./foo" is relative, but relative import paths are not supported in module mode
```

　またパッケージ内に_test.goのようなテスト用ファイルを作って一括で実行しようとした場合も同様のエラーが起こりました。

```shell
>go test
go: cannot find main module, but found .git/config in D:\xxx\udemy-gogo-golang
        to create a module there, run:
        cd .. && go mod init
```


　調べて分かったのですが、パッケージを指定しても環境変数`GOROOT`や`GOPATH`の下しか見てくれないんですね。元々はGo言語の開発は`GOPATH/src/`にプロジェクトごとのディレクトリを掘って作るのが普通だったそうです。  
　これは他言語で開発してきた方にはけっこう受け入れがたいのではないでしょうか。僕も最初に知った時、例えばWindowsであれば  
`C:\Users\<username>\go\src\some_project\....` でしか開発できないとか正気か？と面食らいました。(笑)

　このへんも進化しており、外部パッケージの管理システムGo Modulesが途中からGo本体に採用されています。2018/8リリースの[Go 1.11](https://go.dev/blog/go1.11)でGo Modulesが登場、このころはGOPATHモードとModuleモードが両方。  
2019/3リリースの[Go 1.13](https://go.dev/blog/go1.13)で正式にModuleモードがデフォルトになっています。また過渡期ではGoが使う環境変数`GO111MODULE`があり、手動で"on"という値を入れる必要があった時期もありましたが、その後デフォルト値が"on"になり、もうこの環境変数自体を気にしなくてよくなりました。

　**2019年3月～**ということで、このGo Modulesの話はWeb上の情報、学習プラットフォーム、商業書籍でも触れられていないことがけっこうあります。ググる際は情報の発信日にご注意ください。  
　Goに入門した方のブログ記事などを見ると、このGo Modulesの話は**コードを書き始める前にもっと早く知りたかった**...という声を割と見かけます。(僕もです！ｗ)


##### Go Modulesを使ってプロジェクトを作る(GOPATH配下)

　Go言語では同じディレクトリ配下のファイルに置かれ、ソース先頭に`package foo`などと記されたものが同一パッケージの扱い。そして複数のパッケージの集合で一緒にリリースされ、ひとつの実行ファイルに入るものを「モジュール」と呼びます。このエントリで「プロジェクト」と言っているものとイコールになります。


　安直ですが勉強用に`udemy-gogo-golang`というリポジトリがあり、これをマシン上にクローンしたとします。
まずは `{環境変数GOPATHの場所}/src/udemy-gogo-golang` に取ってきたとします。

```shell
CMD> cd {環境変数GOPATHの場所}/src/udemy-gogo-golang
CMD> go mod init udemy-gogo-golang (モジュール名を入力)
go: creating new go.mod: module udemy-gogo-golang
go: to add module requirements and sums:
        go mod tidy
```

　最初に`> go mod init` でモジュールが作られます。この時、GOPATH配下の場所で作った時に限り、モジュール名を省略しても自動でフォルダ名が使われました。  
　このコマンド実行でそのフォルダ配下がGo Modules管理下に入り、プロジェクトのルートに `go.mod` というファイルが自動で作られます。go.mod の中身は以下のようなテキストファイルです。

```json
module udemy-gogo-golang

go 1.18
```

これにより他のパッケージも参照でき、`> go test`でまとめてテスト実行した場合もフォルダの階層関係が正しく認識されて動くようになりました。


##### Go Modulesを使ってプロジェクトを作る(任意の場所)

今度は `{任意の場所}/udemy-gogo-golang` にクローンしてきたとします。

```shell
CMD> cd {任意の場所}/udemy-gogo-golang
CMD> go mod init udemy-gogo-golang //ここではモジュール名必須！
go: creating new go.mod: module udemy-gogo-golang
go: to add module requirements and sums:
        go mod tidy
```

ふつう入力するので問題ないと思いますが、`> go mod init` の後のモジュール名は入力しないと自動で認識はしてくれませんでした。GOPATH配下の時とまったく同じように`go.mod` ファイルが作られます。

```go
udemy-gogo-golang
  sectionXXX/
    foo/
      bar.go // Bar関数が定義されている
    main.go
  go.mod // ルートに自動生成
```

```go
main.goの中身
package main //sectionXX
import (
	//VSCodeだと自動でパスが挿入、手動でエイリアス定義が必要
	foo "udemy-gogo-golang/sectionXX/foo"
	"fmt"
)

func main() {
	fmt.Println("fooの中のBar関数を実行", foo.Bar())
}
```

上記のようにしてコンパイルエラーも起こらず実行できました。なおimport文のところは、GOPATH配下にプロジェクトを作成した場合は`"foo"`でも行けたのですが任意の場所にプロジェクトを作った場合は`"udemy-golang-webgosql/sectionXX/foo"`のようにルートからの絶対パスが自動で入りました。これは例がアレなのでこういうパスになっていますが、実際の開発で外部パッケージを使う場合は`"github.com/accountname/go-xx-tool"`のようなパスが入ることになります。  
　Go側で階層構造を認識する際にこの情報を使っているようです。書籍『みんなのGo言語』でも、サブパッケージをインポートする際には `./foo`のような相対パスでなく、上記のような絶対パスを使うよう推奨されています。




*_test.go のテスト用ファイルを作った場合も、Go Modulesで認識されていれば正常に動かすことができます。

```shell
> cd udemy-gogo-golang/sectionXX
> go test
PASS
ok      udemy-gogo-golang/sectionXX 5.580s
> go test ./foo //パッケージに限ったテストは、./の相対パス。
?       udemy-gogo-golang/sectionXX/foo    [no test files] //_test.goがない時
> go test ./... //全パッケージの全テストファイルを実行する場合
```

　他にもGo Modulesにはいくつかのコマンドがあり、生成される`go.mod`や`go.sum`などのファイルは基本的には手動で弄らなくてもコマンド実行時で自動で書き換わり、モジュールを管理してくれます。JavaScriptのnpmでルートに生成されるpackage.jsonと同じような動きですね。"go modules 使い方"などでググると情報があります。以下のページなどが参考になります。

https://qiita.com/uchiko/items/64fb3020dd64cf211d4e

https://zenn.dev/optimisuke/articles/105feac3f8e726830f8c

https://nishinatoshiharu.com/go-modules-overview/

https://blog.framinal.life/entry/2021/04/11/013819


##### おまけ：main関数が複数あるプロジェクトの場合

　業務だとそんなプロジェクトありませんが、学習用のコードだと短いファイルにその都度main関数を書いたり、同じような構造体を何回も定義したりすることがあるかもしれません。

```shell
udemy-gogo-golang/
  section1/
    main.go // mainパッケージ。中にmain関数があったり、構造体Userがあったり
    main2.go // ここにもmain関数、同じ構造体...
  section2/
    main1.go // 同じ
    main2.go // 同じ
```

　上記の構造で学習が進むたびに1ファイルごとに`> go run` したりするのは問題なかったのですが、Go Modules を使ってルートにgo.modファイルができたとたんにフォルダ内の構造が正しく認識され、チェックが走ってmainパッケージが複数ある！ main関数が複数ある！ 同名の関数が複数ある！ 同じ構造体が何度も定義されてる！と厳格なことで知られるGoコンパイラ様が大量のエラーを吐くようになりました。  
　まあそりゃそうだという話なんですが、Go Modulesで管理されているモジュール(プロジェクト)だと認識されるとこういう動きになるんですね。このケースでは下記のようにさらにフォルダを掘ることで、mainパッケージやmain関数がそれぞれのファイルごとにあっても問題なく動作するのが分かりました。

```
udemy-gogo-golang/
  section1/
    main1/
      main.go // mainパッケージ。中にmain関数があったり、構造体Userがあったり
    main2/ // フォルダ名は自由
      main2.go // ここにもmain関数、同じ構造体...
  section2/
    sec2-1/
      main21.go // 同じ
    sec22hoge/
      main22.go // 同じ
      foo/
        bar.go // mainの下のサブパッケージも認識できた
```

```
CMD> cd udemy-gogo-golanng/section2/sec22hoge/
CMD> go run main22.go // コンパイル&実行できる
```
　Goの言語仕様ではディレクトリ名(フォルダ名)とパッケージ名が等しくなくても動くので、こういう動作になるわけですね。いろいろ試すと面白いです。

　以上、VSCodeでGo言語開発環境を構築する話でした。  

この記事の元になったメインのブログでは他にも様々な記事を載せているので、こちらもどうぞお立ち寄りくださいませ。

https://iwasiman.hatenablog.com/entry/20220516-go-env-on-vscode