🥗

go: internal package

2022/09/17に公開

概要

個人の備忘録としてGoのinternal packageについてまとめていきます。
(2014年当時の公式情報をもとにしています)

参考
https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit
https://go.dev/doc/go1.4#internalpackages

internal package概要

internal packageとはパッケージ定義の仕組みでinternalと呼ばれるパッケージの近くのパッケージのみがimportでき、その他のパッケージからはimportできないパッケージの仕組みのことです。

a mechanism for defining packages that can only be imported by nearby code, not by any other package.

参考
https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit

背景

Goのプログラムの構造として複数のパッケージでexportしたAPIを相互にやり取りすることで1つのプロジェクトを作ります。しかし、この作りだと全てのパッケージがimport可能となります。
この仕組みではライブラリやコマンドを実装する際に不用意なexportを生みます。パッケージ分割するのに十分な大きさのファイルがあったとしても、パッケージ分割するためには他パッケージからメソッドを呼び出せるようにexportする必要があり、すなわちライブラリやコマンド利用者にも関係のない内部ロジックを呼び出せてしまう状況を作ってしまいます。パッケージの可視性を制限できるようにすることでこの課題を解決できます。

パッケージ可視性の制限に関するGoのmainリポジトリで3つのモチベーション

  1. Goの標準ライブラリではexportされてない重複したいくつかのコードがあります。理由は共有化するにはそれらの関数をexportしなければならないからです。
    例えば、パッケージnet/httpnet/http/httputilはパース関数をいくつか共有していて、func itoaがいくつかのパッケージにそれぞれあります。同様に、パッケージcmd/nmcmd/objdumpは同じファイル読み取りコードが含まれています。

  2. パッケージsc.io/x86/x86asmrsc.io/arm/armasm中のコードのサブセットがパッケージcmd/objdumpによって参照されています。標準パッケージの一部とすることなくこれらの必要なパッケージをコピーできるようにしたいこと。

  3. GoでGoのコンパイラーが実装される際、Goのメインリポジトリには存在するけど標準ライブラリではないコンパイラ用のライブラリパッケージを書く方法が必要です。

これ以外のリポジトリに取り組む開発者は似たような問題に遭遇していました。

Proposal

internalを含むパッケージのimportはinternalパッケージの親のディレクトリツリーのルートのパッケージのみがimportできるようにします。

An import of a path containing the element “internal” is disallowed if the importing code is outside the tree rooted at the parent of the “internal” directory.

  • パッケージ/a/b/c/internal/d/e/fのコードは/a/b/cのディレクトリルートのみからimportできます。パッケージ/a/b/gのコードからはimportできません
  • $GOROOT/src/pkg/internal/xxxは標準ライブラリ($GOROOT/src/)中のその他のコードからのみimportできます
  • $GOROOT/src/pkg/net/http/internalは標準ライブラリnet/httpnet/http/*パッケージのみからimportできます
  • $GOPATH/src/mypkg/internal/foo$GOPATH/src/mypkgのコード中のみからimportできます

Discussion