我尝试了解如何使用“内部”包来组织代码。让我展示一下我的结构:
project/
internal/
foo/
foo.go # package foo
bar/
bar.go # package bar
main.go
# here is the code from main.go
package main
import (
"project/internal/foo"
"project/internal/bar"
)
project/
在GOPATH树之外。无论我尝试从main.go
导入的路径是什么都行不通,唯一正常工作的案例是import "./internal/foo|bar"
。我认为我做错了什么或者一般来说“内部”包装的想法是错误的。请问有人能让事情更清楚吗?
更新
上面的示例是正确的,我唯一需要的是在project/
下放置$GOPATH/src
文件夹。因此,如果我们只从project/internal/foo|bar
子树导入而不是从外部导入,那么project/
之类的导入路径是可行的。
答案 0 :(得分:13)
包裹必须位于$GOPATH
中才能导入。您使用import "./internal/foo|bar"
提供的示例有效,因为它执行本地导入。 internal
只会使得internal
目录中不共享公共根目录的代码无法在internal
内导入包。
如果你把所有这些都放在gopath中,那么尝试从OuterFolder/project2/main.go
这样的其他位置导入OuterFolder
包含project
和project2
,然后import "../../project/internal/foo"
会失败的。由于不满足此条件,它也会以import "foo"
或您尝试的任何其他方式失败;
如果是,则不允许导入包含元素“internal”的路径 导入代码在树的外部,以树的父节点为根 “内部”目录。
现在,如果您拥有路径$GOPATH/src/project
,那么您可以在import "foo"
内执行import "bar"
和$GOPATH/src/project/main.go
,导入就会成功。但project
下未包含的内容无法导入foo
或bar
。
答案 1 :(得分:10)
在Go v1.11及更高版本中引入了模块,您无需在$ GOPATH / src中指定项目路径
您需要通过创建go.mod文件来告知Go每个模块的位置。请参阅go help mod
文档。
以下是操作方法的示例:
project
| go.mod
| main.go
|
\---internal
+---bar
| bar.go
| go.mod
|
\---foo
foo.go
go.mod
project / internal / bar / go.mod
module bar
go 1.14
project / internal / bar / bar.go
package bar
import "fmt"
//Bar prints "Hello from Bar"
func Bar() {
fmt.Println("Hello from Bar")
}
project / internal / foo / go.mod
module foo
go 1.14
project / internal / foo / foo.go
package foo
import "fmt"
//Foo prints "Hello from Foo"
func Foo() {
fmt.Println("Hello from Foo")
}
project / main.go
package main
import (
"internal/bar"
"internal/foo"
)
func main() {
bar.Bar()
foo.Foo()
}
现在最重要的模块 项目/go.mod
module project
go 1.14
require internal/bar v1.0.0
replace internal/bar => ./internal/bar
require internal/foo v1.0.0
replace internal/foo => ./internal/foo
在这里耦合事物:
go: internal/bar@v1.0.0: malformed module path "internal/bar": missing dot in first path element
这就是为什么需要在replace
告诉Go去哪里找到它的原因,这就是关键!
replace internal/bar => ./internal/bar
现在,执行代码时,您将拥有
Hello from Bar
Hello from Foo
答案 2 :(得分:6)
以下方式更具可扩展性,尤其是当您计划构建多个二进制文件时
github.com/servi-io/api
├── cmd/
│ ├── servi/
│ │ ├── cmdupdate/
│ │ ├── cmdquery/
│ │ └── main.go
│ └── servid/
│ ├── routes/
│ │ └── handlers/
│ ├── tests/
│ └── main.go
├── internal/
│ ├── attachments/
│ ├── locations/
│ ├── orders/
│ │ ├── customers/
│ │ ├── items/
│ │ ├── tags/
│ │ └── orders.go
│ ├── registrations/
│ └── platform/
│ ├── crypto/
│ ├── mongo/
│ └── json/
cmd/
中的文件夹代表您要构建的二进制文件数。
答案 3 :(得分:0)
还要检查:在使用外部导入的对象类型时:确保使用它们所在的名称空间为它们添加前缀。作为golang新手,我不知道自己拥有这样做,并且想知道为什么VS Code在保存时仅删除了我的导入(不使用)。这是因为我必须使用名称空间名称给导入的对象加上前缀:
Example:
import (
"myInternalPackageName" // works fine as long as you follow all instructions in this thread
)
//Usage in code:
myInternalPackageName.TheStructName // prefix it, or it won't work.
如果不将名称空间前缀放在对象/结构名称之前,则VS代码只会删除未使用的导入,然后仍然会出现错误:“找不到TheStructName” ...令人困惑,我不得不通过命令行在没有VS代码的情况下进行构建以了解这一点。
重点是:在实际使用内部包中的结构时,我必须指定包前缀。
如果在使用导入的对象时不想使用限定词前缀,请使用:
import . "thePath" // Can use contents without prefixing.
参考:What does the '.' (dot or period) in a Go import statement do?