Go言語初めの一歩踏んでみましたというお話。

はじめに

こんにちは, 42tokyo Advent Calendar 2021の22日目を担当する, 在校生のrakiyamaです。
この記事では, Go言語の学習で私が躓いたことについてまとめました.
環境 : Mac
現在のGoの最新バージョン : 1.17
Goのインストールはbrew install goで行いました(その他インストール方法参考).

本記事で載せている参考リンクは英語の記事もありますが(気合い(DeepL)で読むと意外と読めます),
探せば日本語版もあるかもしれません.

基本用語

  • ダウンロード
    意味: 対象物を持ってくる.
    例: 利用したいプログラムのソースコードを自分のパソコンにコピーする.

  • インストール
    意味: 対象物を実行できるようにする(この過程でダウンロードやコンパイルを含むこともある?, 目的はあくまで実行できるようにすること).
    例: バイナリファイル(実行形式のプログラム)を適切なディレクトリ(/binなど)に配置する.

  • バージョン
    Goの話でバージョンという言葉が出たら以下を指すことが多いみたいです.

    • Go言語自体のバージョン
    • レポジトリやモジュールのバージョン
      ※レポジトリとモジュールがよく分からなければひとまずレポジトリは忘れて, モジュールを"プログラムの一つのまとまり"として先に進みましょう.

    仕様に変更が加わってGo1.10からGo1.11になりましたよ〜っていう感じで更新を識別するための表記のことですね.

  • GOPATH

    • パスの環境変数.
    • 初期設定でホームディレクトリ/goを指す.
    • GOPATHの指すディレクトリは何かと使われます.
      例えば, Goのコマンドを使って何かをダウンロードやインストールすると, 対象物はGOPATH配下の適当なディレクトリに配置されます.
      さらにGOPATHモードではソースコードもGOPATH配下で管理します.
    • go env GOPATHをターミナルで打つと内容が確認できます.
  • パッケージ

    • 1つ以上のファイルをまとめたもの.
    • 1つのパッケージを構成するファイルは同じディレクトリに置き, 一緒にコンパイルする.
    • 例として, 文字列操作プログラムを作る場合を考えます.
      「文字列を大文字変換などする」機能を自分で用意して自作のconvertstringsパッケージにまとめます.
      また, 「文字列を別の言語に翻訳する」機能を既に外部で公開されているstranslateパッケージから利用するとします.
      すると私のパッケージのまとめ方はこんな感じです. f:id:rakiyama0229:20211230090924p:plain ファイルを作成する際にはそのファイルがどのパッケージに属するのかをファイル冒頭に明記します.

GOPATHとモジュール

Go言語ではファイルの管理方法が定められていて, 以下の二つがあります.

  • GOPATHなるものを基準にする方法(GO1.10まで)
  • モジュールなるものを基準にする方法(GO1.11から徐々に標準となった)

Go言語で開発する際のルールと捉えています.
私はこの管理方法がバージョンによって変わることを知らなかったので, 学習を進める中でめちゃんこ混乱しました.
仮にここではGOPATHに従う方法をGOPATHモード, モジュールに従う方法をモジュールモード(公式ではmodule-aware modeとか書いてあります) として話をします.

GOPATHモード

GOPATHモードでは, 以下のようなディレクトリ構造をGOPATH配下に作成し, ファイルを管理します.
このディレクトリをルートディレクトリとして作業を進めるため, ワークスペースとも呼ぶみたいです.

GOPATH/
    bin/
        バイナリファイル
    pkg/
        パッケージのオブジェクトファイル
    src/
        ソースコード (全てのソースコードはここに)

開発を進める際のルートディレクトリは常にGOPATHであり, ソースコードはsrcディレクトリの中で管理します.
つまりGOPATHの中でのみ開発を進めるという特徴があります.
ワークスペースのsrc内にファイルを作成しましょう」みたいな話が参考サイト等で出てきたら,
GOPATHモードが前提の話だと思います.
実際どのようにプログラムを作成するのかは以下リンクが分かりやすいです.
go.dev

モジュールモード

ここで新たにモジュールという概念が出てきます.

  • モジュール
    • 1つ以上のパッケージをまとめたもの.
      実体は, パッケージ + バージョン管理情報.
      • バージョン管理とは
        依存関係にあるモジュールのバージョンや
        使用しているGoのバージョンの情報を
        (ファイルに明示的に記述しておくなどして)管理しておくこと.
    • 例として, 先ほどのパッケージをモジュールでまとめたイメージを載せます.
      ポイントはバージョン管理によりモジュールの依存関係がまとめられていることです. f:id:rakiyama0229:20211230092403p:plain 仮に作ったstringmanipモジュールのバージョンを0.0.0とすると,
      stringmanipモジュール0.0.0のビルドの再現性がバージョン管理によって確保されています.
      GOPATHモードではこのバージョン管理の仕組みが標準で用意されていなく苦労してたみたいですが, どのように対処していたのか詳しくはわかりません.

モジュールモードでは, ファイルをパッケージにまとめ,
さらにパッケージをモジュールとしてまとめてプログラムを作成します.
ルートディレクトリにgo.modというバージョン管理ファイルを用意すれば,
そのディレクトリ自体はどこに位置しても大丈夫です(goコマンドが使えるところならおそらく).
要はGOPATHモードに比べて,
バージョン管理の仕組みが用意され, 開発に関しても任意のディレクトリで行えるようになったので,
便利になったということです.
ちなみにGOPATHは引き続きインストール先などで利用されています.
参考リンク GOPATH に(可能な限り)依存しない Go 開発環境(Go 1.15 版)

モジュールのディレクトリ構造

ここでは先ほどのモジュールのイメージを元に実際のディレクトリ構造を載せます.
とてもざっくり説明なので, 詳しくは以下のリンクを参考にしてください.
go.dev

自作パッケージを用意して, main.go内で使う

ディレクトリ構造

.
├── convertstrings //自作パッケージconvertstringsを含んだconvertstringsディレクトリ
│   ├── lower.go
│   └── upper.go
└── main.go

↓バージョン管理ファイル(go.mod)作成

//ターミナル
$> go mod init stringmanip(モジュール名)
$> tree
.
├── convertstrings
│   ├── lower.go
│   └── upper.go
├── go.mod
└── main.go

↓自作パッケージ(convertstrings)のimport

//main.go

package main

import "stringmanip/convertstrings"  //使いたいパッケージのディレクトリパスを指定
                                     //go.modのある場所をモジュール(stringmanip)全体のルートディレクトリとして考える

func main () {
     convertstrings.Upper("string")
}

外部パッケージも使う

↓外部パッケージのimport

//main.go

package main

import "stringmanip/convertstrings"
import "github.com/example/language/translate"  //リモートレポジトリから使いたいパッケージまでのディレクトリパス指定

func main () {
     convertstrings.Upper("string")
     translate.Spanish("string")
}

↓go.modの編集, go.modのあるディレクトリで以下を実行

//ターミナル
$> go mod tidy

main.go内でパッケージ名とそのディレクトリ名が同じで分かりにくですが,
importパスで指定しているのはディレクトリで
convertstrings.Upper()のように使用する際に先頭に付けているのはパッケージ名です.

モード操作

現在のモードの確認

$> go env | grep GO111MODULE
GO111MODULE="on"   #常にモジュールモード
GO111MODULE="off"  #常にGOPATHモード
GO111MODULE="auto" #go.modファイル(バージョン管理情報をまとめたファイル)がカレントor親ディレクトリにある時のみモジュールモード

モードの変更

$> go env -w GO111MODULE=auto # autoに変更

Goについて参考になった情報

まとめ

ファイルやディレクトリ構成はHow to Write Go Code - The Go Programming Languageを参考にモジュールで考えれば良いんだなーということが伝われば幸いです!!
どこか間違った点があればご指摘頂けると嬉しいです。
明日はrsudoさんがイケイケ開発環境構築について記事を書いてくれる予定です!お楽しみに!