フューチャー技術ブログ

Go Tips連載4: GoDocの読み方

Go Tips連載の第4弾目です。

新しいプログラミング言語を学ぶときは、まず、情報の取得の仕方を覚えておくと学習効率が上がります。検索して、日本語で誰かが解説している二次情報を探すのもいいのですが、古くなって使えなかったり、人気のある情報に偏ってしまって、ちょっと混み入った話になると情報が見つからなくなったりしがちです。多少素っ気なく感じるかもしれませんが、一次情報を読み解く方法を知っておくと良いでしょう。本家が作ったものではなくても網羅的に、リファレンスとなる心意気で書かれたものもたまにあります。

種類 区分 翻訳 解説
Tour of Go 一次 あり Goの書き方チュートリアル
Effective Go 一次 メンテされているものはなし? Goのコードはこう書くべきといった指標
The Go Blog 一次 あり 新し目の機能など大きめのトピックについて設計思想とか解説が書かれることがある
Go Doc 一次 ムリ 標準・サードパティのライブラリのリファレンス
Go Wiki 一次 文法の解説、コミュニティ情報などいろいろあったり
Release Notes 一次 Qiita等に有志が訳したものがあがる 新バージョンの差分はここ
Go by Example 二次 あり 細かい書き方のサンプルコード集
Java to Go in-depth tutorial 二次 あり 既存の言語ユーザーのためのGo入門

今回は、GoDocについて取り上げます。

長いので3行にまとめると

  • GoDocのリファレンスはライブラリにとっては一次情報だが、使い方などはリポジトリのREADMEに書かれることも多い
  • 構造体やインタフェースは、リファレンスだけを見ても使い方がわからないこともあるので、分からないからといって落ち込まないでください
  • サンプルコードも合わせて確認して使い方を確認すると良いです

GoDoc

GoDocは標準ライブラリや、他のサードパティ製ライブラリのドキュメントをホストしているウェブサイトになります。チュートリアルなどは一度クリアしたら見返すこともあまりないでしょうけども、GoDocはGoで開発している限り、ずっと参照し続けることになります。GoDocの読み方を知っておくことは、Go力をあげるには必須なスキルだと思います。

GoDocのURLは以下の通りの場所

以前はこちらで、検索エンジンでドキュメントを検索するとこっちのドメインが表示されると思いますが、上のツールバーの、always use pkg.go.devをクリックすると、上記のURLにリダイレクトされます。

書いてある内容

最近人気のchiのドキュメントを表示してみました。

上から、importするときの名前、パッケージ名、バージョンなどが書かれています。
その下は更新日時、ライセンス、モジュールのページです。

モジュールというのはサブディレクトリとかも含めた共通のルートページで、ソースコードがおいてあるリポジトリのリンクや、リポジトリのReadmeなどが書かれています。

その下のタブは、色々な情報へのハブになっています。

  • Doc: 実際のドキュメント
    • リファレンスと概要
  • Overview: モジュールと同じ
  • Versions: 今まで公開されたバージョン一覧
  • Imports: どのパッケージに依存しているか?
  • Imported By: どのパッケージから利用されているか? 人気なものほど多い
  • License: ライセンス

まず読むところ

いきなりリファレンスを読みたい人もいるかもしれませんが、ふつうは概要を理解したいですよね? Goの場合は2箇所あります。

  • DocのOverview
  • モジュールページのREADME

Goのソースコードの中のコメントとして書いたドキュメントであれば前者に、リポジトリのREADMEに書かれた内容であれば後者に概要が書かれます。標準ライブラリの場合は前者しかないですが、サードパーティの場合、後者の方がリッチに丁寧に書かれることが多いように思います。どちらに詳しい情報が書かれるかはパッケージの実装者次第なので両方見て見ると良いでしょう。

サンプルコード

概要を把握したところで、どう書くのかを見て見たい人もいるでしょう。サンプルコードを探して見ましょう。DocのOverviewには3箇所、サンプルが置かれる可能性のある場所があります。

  1. DocのOverviewや各メソッドの説明: Goのコメントの流儀に従ってフォーマットされているサンプル
  2. パッケージ全体のサンプルは、(あれば)DocのOverviewの末尾にサンプルコードが置かれる
  3. 各構造体や関数、メソッドのサンプルは、それぞれのリファレンスの中に書かれる

chiはこの最初の1の形式のドキュメントが書かれていました。

2つ目と3つ目はchiにはなかったので、標準ライブラリのdatabase/sqlパッケージのドキュメントを見て見ましょう。Overviewの下に、パッケージレベルのサンプルコードが2つあることがわかります。▼をクリックすると表示されます。

メソッドなどのドキュメントは、それぞれの説明のテキストの後ろにサンプルが置かれています。

Goの公式ドキュメントにおけるサンプルというのは、サンプルの形を模したテストコードだったりします。もし、期待するコードがなければ、リポジトリのサンプルを見て見てもいいでしょう。_example.goあるいは、_test.goといったファイルに入っています。Exampleから始まる関数がサンプル、Testから始まる関数がテストです。

サードパーティのパッケージで、これらのどこにもなかった場合、モジュールページのREADMEにある可能性があります。こちらはMarkdownなので、サンプルでもなんでも自由に書けます。この中にサンプルも書かれることが多いです。

ライブラリリファレンス

ライブラリリファレンスはDocタブのIndexという項目以下にあります。ドキュメントのメインコンテンツです。

リファレンスは以下の順番に並んでいます。

  • Constants: 定数一覧
  • Variables: 変数一覧
  • func …の各項目: パッケージ内で定義されている関数
  • type …の各項目: パッケージ内で定義されている型(構造体、名前付き型、インタフェース)

定数、変数は名前とか型を確認するぐらいだと思いますし、関数や型の説明の中で参照されるので、そのタイミングで読むことになると思います。関数も、読み方としては特に難しくはないと思います。ここではtypeの各項目について紹介します。

要注意ポイントとしては、GoDocはそのサービスが起動しているOS向けの要素しか表示しません。osパッケージにはWindows用の要素だったりとかもあったりしますが、pkg.go.devはLinuxで動作しており、リファレンスもLinuxで有効な項目のみが表示されます。ファイル操作のバッチのプログラムとかを作成する場合には注意してください。

構造体

typeのうち、構造体は結構、他の言語のユーザーからすると初見殺しな気がします。たいてい、ドキュメントを見ると「あ、このメソッドを呼んであげれば、やりたいことができそうだ」というのは把握できると思います。しかし、そのメソッドをどう呼んであげればいいのか、で詰まることがあります。ベテランであっても。

構造体の説明はまずはGoの定義コードほぼそのまま(privateな要素以外)が書かれています。

さて、この構造体のインスタンスを作って使うにはどうすればいいでしょうか? Goの文法的には3通りあります(newはあんまり見ないので省略)。

// cはインスタンス
var c Context
// cはインスタンス(パラメータの初期化もできる)
c := Context{}
// cはポインタ(パラメータの初期化もできる)
c := &Context{}

それ以外に、リファレンスを見ると、レシーバーがついてないメソッドが構造体のメソッド一覧の先頭にいることがわかります。コンストラクタ関数とかファクトリ関数と呼ばれているもので、必要な属性を持ったインスタンスを生成し、そのポインタや実体を返すものです。

type Context
func NewRouteContext() *Context
func RouteContext(ctx context.Context) *Context

これらのインスタンスの作成方法のうち、どの手法で作ればいいのか、というのはそのライブラリによって違います。

  • ファクトリー関数があるものであれば、ほぼそれでいいはず
    • メンバー変数にprivateなものがあり、ファクトリー関数でのみ初期化という場合は確度が高い
    • ただし、その中にもGoの文法で作っても動作するハイブリッドなものとかもある
  • 自分で作らず、ライブラリの内部で作ってインスタンスだけ渡ってくるものもある。net/httpのサーバーのハンドラのRequestとか
  • Goの文法で作成するもののうち、コピー不能でポインタ型で取り扱わないといけないものもあり

どの手法で構造体のインスタンスを作ればいいのか、というのはサンプルコードとかを見て判断することになります。

読み手の気持ちを考えると、何かしらのライブラリを作って構造体を提供するときは、ファクトリー関数を作って提供しましょう、というのがベストプラクティスになる気がします。

名前付き型(named type)

Goでは、メモリ上の実態は同じだけど、別の型として振る舞わせることが可能です。例えば、画面に表示しようとするとアスタリスクに塗りつぶす文字列型、みたいなことができます(これは %v とかでダメなので簡易実装です。このまま利用はしないでください)。

type SecretString string

func (s SecretString) String() string {
result := make([]byte, len(s))
for i := 0; i < len(s); i++ {
result[i] = '*'
}
return string(result)
}

これも、例えば列挙型のようなものを作ったりするには便利な方法です。しかし、リファレンスを見ても、この型をどう作るのかの説明がされることはありません。これはどうやってインスタンスを作ればいいでしょうか? 作り方としては次のような感じになりますね。

// 型キャスト
s := "important credential" // sはstring
ss := SecretString(s) // ここでキャスト。ssはSecretString

// 最初からその型で宣言
var ss SecretString = "secret password"

これで、どちらもssがSecretString型になるので、リファレンスに書かれているメソッドが使えるようになります。

場合によっては、Constantsの定数として事前に定義されているもののみを使うという流儀のライブラリもあります。その場合はインスタンスを作らずに、importして利用するだけでいけます。どの方法でインスタンスを利用すればいいのかは実装次第なので、サンプルコードとかを見てみると良いでしょう。

インタフェース

インタフェースも、便利なメソッドは見つけたが、どうやってインスタンスを取得すればいいのかわからない筆頭です。これはベテランでも初見では分からないこと多数です。分からないからといって落ち込まないください。みんな分からないのです。

インタフェースは、とあるインスタンスが、そのインタフェースで定義されているメソッドを持っている、というだけの情報しかありません。主に4通りあります。2番目のものはウェブサイドではなくローカルで動作するgodocコマンドの解析機能を利用すると発見できます。発見できた後は、この上の節の構造体をどうする問題に遷移します。最後のものは今のところ僕が見たことがあるのはnet/httpのPusherとかだけですので無視してもいいかもしれません。

  • 自分で作る
    • そのインタフェースを満たすメソッドを持った構造体をユーザーが作成してそのライブラリの他のメソッドに渡せばいいのか
  • 既成の構造体がこのインタフェースを満たしている
    • 何かしらの構造体のインスタンスを作ると、そのインタフェースを満たして、そのインタフェースの型の変数に代入したり、メソッドの引数に渡せるようになる
  • ライブラリが作成してくれるもの
    • net/httpのResponeWriterなど
  • インタフェース間のキャスト
    • 特定のインタフェースを型アサーションでキャストすると、そのインタフェースが得られてメソッドが利用できるようになる

これのどれに当たるかは、リファレンスだけを見てもさっぱり見えてこないこともあります。これも素直に、サンプルコードなどを参照するようにした方が良いでしょう。

これを読んでいる上級者の方々は、もしインタフェースを実装して他の人に使ってもらおうと思っているのであれば、必ずサンプルコードを見える位置に置いてあげてください。私からの一生のお願いです。

引数のinterface{}

Goは静的型付け言語ではあるものの、リフレクションを使ってある程度動的型付け言語のような動作をさせることができます。ライブラリによっては、「ある程度どんな型でも受け付けますよ」ということがあります。Goのinterface{}型は他の言語でいうanyだったりvoid *だったりします。例えば、GoでJSONを扱おう、となるとこのinterface{}が登場します。

で、こいつをどうやって作ればいいのか、というのもリファレンスの関数の定義をいくらにらめっこしても出てきません。数字でも文字列でも受けつけますよ、ということでinterface{}にしているかもしれませんし、上記のJSONの場合は、自作の構造体を作る必要があります。その構造体のフィールドにはタグを付与します。このタグにはDSLというかルールがあるのですが、このルールは関数の引数とかのリファレンスを穴があくほど見ても分かりません。ライブラリの作者が自然言語で解説を書く必要があります。またJSONとかもタグを書かなかった場合のデフォルト動作みたいなのもあったりします。

type MyStruct struct {
Name string `json:"name"`
SessionToken string `json:"-"`
NickName string `json:"nickname,omitempty"`
Age int `json:"num,string"`
}

このinterface{}は自然言語部分の説明とかサンプルコードを参照するのが良いでしょう。

まとめ

プログラミングを学ぶと、一次情報にあたれ、みたいなことはよく言われます。今回は、その中でGoDocの読み方について解説しました。むしろ、GoDoc読んでもここは難しいぞ、というのを理解してもられば、GoDocとうまく折り合いをつけてやっていけるようになると思います。

上級者になるとリファレンスを見て使い方を想像したり、生のコードを解析してなんとかできるようになりますが、初心者だと使い方を知る部分も結構難しいです。サンプルコードも探しながら、挑戦して見てください。経験を積めば、少しずつ予想ができるようになってきます。