Go の import cycle not allowed (循環参照) の対応

import cycle not allowed (循環参照) について

例えば、
package A 内のどれかのファイルで import b をし、
package B 内のどれかのファイルで import a をすると、

a → b → a → b... となり、

「import cycle not allowed」というエラーが起きる。
これは「循環参照」といって import した先々を繋いでいった場合に、
終わりが来ればエラーは起きないのだけれど、
import の先々でお互いを import していった場合に、
このようなエラーが起きる。

上記だと2つの package だけなので分かりにくいけど、

例えば、
package A 内のどれかのファイルで import b をし、
package B 内のどれかのファイルで import c をし、
package C 内のどれかのファイルで import a をすると、

a → b → c → a → b → c → a... となり、

import 先で循環 (cycle) が起きる。

つまり、
「import cycle not allowed」
「import の循環 (cycle) は許可されてない (not allowed)」
ということになる。

循環参照が起きるパターン

ファイル構成

  • /path/to/go/src/psychedelicnekopunch/go-sample
a/
  ├ A.go
  └ A2.go
b/
  └ B.go
main.go

main.go

  • package a を imoprt して A.Get() を呼ぶ。
  • A.Get() 内では return B.Get() をしている。
  • B.Get() 内では return A2.Get() をしている。
  • A2.Get() 内では return "success" になる。
package main

import (
	"fmt"
	"psychedelicnekopunch/go-sample/a"
)

func main() {
	a := a.NewA()
	fmt.Print(a.Get())
}

a/A.go

package a

import (
	"psychedelicnekopunch/go-sample/b"
)

type A struct {}

func NewA() *A {
	return new(A)
}

func (a *A) Get() string {
	b := &b.B{ A2: NewA2() }
	return b.Get()
}

b/B.go

  • package a を import しているので、循環参照が起きている。
package b

import (
	"psychedelicnekopunch/go-sample/a"
)

type B struct {
	A2 a.A2
}

func (b *B) Get() string {
	return b.A2.Get()
}

a/A2.go

package a

type A2 struct {}

func NewA2() *A2 {
	return new(A2)
}

func (a *A2) Get() string {
	return "success"
}

解決策1: 循環参照が起きないパターン

ファイル構成

  • /path/to/go/src/psychedelicnekopunch/go-sample
  • b/A2.go を生成する。
    • このファイル内で interface を定義する。
a/
  ├ A.go
  └ A2.go
b/
  ├ B.go
  └ A2.go
main.go

main.go

  • ファイル内容変更なし。

a/A.go

  • ファイル内容変更なし。

b/B.go

  • a/A2.go ではなく、同じパッケージ内の b/A2.go (interface) を定義する。
  • package a を import してないので、この時点で循環参照は起きない。
package b

import (
	// "psychedelicnekopunch/go-sample/a"
)

type B struct {
	// A2 a.A2
	A2 A2
}

func (b *B) Get() string {
	return b.A2.Get()
}

a/A2.go

  • ファイル内容変更なし。

b/A2.go

  • interface を定義する。
package b

type A2 interface {
	Get() string
}

解決策2: interface がよく分からない方向け

  • 循環参照が起きるのであればそれらのファイルを全て同じ package にする。