The desire for an import cycle is usually an indication there is something wrong with the design.
There are 2 general solutions to avoid import cycles:
- If 2 packages are so closely tied and they depend on each other, it is probably better to merge them (or core functionality) into a single package. This avoids import cycles.
- Alternatively, one of the packages should use an interface so it does not depend on the other package. This creates a natural ordering of package layers.
For example, the following contrived example won't compile due to the import cycle:
package main
import "example.com/base"
import "example.com/other"
func main() {
f := &other.Foo{}
_ = base.Process(f)
}
package base
import "example.com/other"
func Process(f *other.Foo) {
for {
if err := f.Frob(); err != nil {
return err
}
}
}
package other
import "example.com/base"
type Foo int
func (f *foo) Frob(name string) error {
if *f == 0 {
return errors.New("completed")
}
*f--
return base.Process(f)
}
This can be fixed by changing base
to use an interface:
package base
type Frobber interface {
Frob(string) error
}
func Process(f Frobber) error {
for {
if err := f.Frob("some file"); err != nil {
return err
}
}
}
This works since *other.Foo
can to passed to base.Process
without the base
package needing to know anything about the other
package.
Another option is to use function variables:
// Given:
package other
import "example.com/base"
type Foo struct {}
func (f *Foo) Frob(name string) error
package base
import "example.com/other"
func Process(f *Foo) error
// Use this instead:
package base
func Process(fn func(string) error) error
// Which enables using:
package main
import "example.com/other"
func main() {
f := &other.Foo{} // NOTE: This needs to be real an initialised.
Process(f.Frob)
}
The example in the question is fundamentally broken beyond the import cycle. It will run out of stack space due to mutual recursion. Ignoring that, you can get it to compile with an untyped func
. Eg:
func File1(fn func(string) string) string {
return "Welcome to message from file 2: " + fn("some file")
}
// Call with:
func something(name string) string {
return "foo/" + name
}
dir1.File1(something)