はじめに
import cycle not allowed
というエラーに遭遇したため、備忘録としてまとめます。
この記事におけるGoのバージョンは以下のとおりです。
% go version
go version go1.24.2 darwin/arm64
問題
パッケージのimport文でimport cycle not allowed
というコンパイルエラーが発生してしまう。
解決策
循環参照が発生してしまっているため、以下のような対応が必要。
- 原因となっている型や機能を他のパッケージへ移動させる
- 原因となっている型や機能を新しいパッケージへ切り出す
- インターフェースを利用して依存関係の向きを変える
- パッケージ構成の全面的な見直し
エラーの詳細
import cycle not allowed
はパッケージ間で循環参照が発生していることを意味します。
循環参照とは、複数のパッケージがお互いをインポートしている状態です。
直接的な循環参照
たとえば次のコードでは、パッケージa
とb
がお互いをインポートしているため、import cycle not allowed
が発生します。
package a
import (
"fmt"
"go-sample/b"
)
func PrintA() {
fmt.Println("Function PrintA called")
b.PrintB()
}
package b
import (
"fmt"
"go-sample/a"
)
func PrintB() {
fmt.Println("Function PrintB called")
a.PrintA()
}
上記2ファイルでgo vet ./...
を実行するとエラーが発生します。
% go vet ./...
package go-sample/a
imports go-sample/b from a.go
imports go-sample/a from b.go: import cycle not allowed
間接的な循環参照
上記は直接的な循環参照ですが、パッケージa
・b
・c
がa→b→c→aのようにインポートしている場合も循環参照となります。
package a
import (
"fmt"
"go-sample/b"
)
func PrintA() {
fmt.Println("Function PrintA called")
b.PrintB()
}
package b
import (
"fmt"
"go-sample/c"
)
func PrintB() {
fmt.Println("Function PrintB called")
c.PrintC()
}
package c
import (
"fmt"
"go-sample/a"
)
func PrintC() {
fmt.Println("Function PrintC called")
a.PrintA()
}
上記3ファイルでgo vet ./...
を実行するとエラーが発生します。
% go vet ./...
package go-sample
imports go-sample/a from main.go
imports go-sample/b from a.go
imports go-sample/c from b.go
imports go-sample/a from c.go: import cycle not allowed
なぜ循環参照がダメなのか?
循環参照があると、Goのコンパイラはパッケージをビルドする際に依存関係を解決できません。
どのパッケージからビルドを開始すればよいか分からないからです。
また循環参照が発生するということは、パッケージ間の依存関係が複雑になりすぎている兆候であり、ソフトウェア設計の観点でも好ましくないです。
そのためGoでは意図的に禁止されています。
おわりに
この記事ではimport cycle not allowed
が発生する原因と解決策についてまとめました。
参考になれば幸いです。
このエラーに頻繁に遭遇するようであれば、パッケージ構成に改善の余地があるかもしれません。
アーキテクチャを見直す機会とするのも有効だと思いました。