如何使用“内部”包?

时间:2015-10-26 16:59:46

标签: go

我尝试了解如何使用“内部”包来组织代码。让我展示一下我的结构:

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/之类的导入路径是可行的。

4 个答案:

答案 0 :(得分:13)

包裹必须位于$GOPATH中才能导入。您使用import "./internal/foo|bar"提供的示例有效,因为它执行本地导入。 internal只会使得internal目录中不共享公共根目录的代码无法在internal内导入包。

如果你把所有这些都放在gopath中,那么尝试从OuterFolder/project2/main.go这样的其他位置导入OuterFolder包含projectproject2,然后import "../../project/internal/foo"会失败的。由于不满足此条件,它也会以import "foo"或您尝试的任何其他方式失败;

  

如果是,则不允许导入包含元素“internal”的路径   导入代码在树的外部,以树的父节点为根   “内部”目录。

现在,如果您拥有路径$GOPATH/src/project,那么您可以在import "foo"内执行import "bar"$GOPATH/src/project/main.go,导入就会成功。但project下未包含的内容无法导入foobar

答案 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

在这里耦合事物:

  1. 您可以在require中使用任何名称。如果需要,可以有项目/内部/栏。 Go认为这是软件包的URL地址,因此它将尝试从网络中提取它并给您错误
go: internal/bar@v1.0.0: malformed module path "internal/bar": missing dot in first path element

这就是为什么需要在replace告诉Go去哪里找到它的原因,这就是关键!

replace internal/bar => ./internal/bar
  1. 在这种情况下,版本无关紧要。您可以拥有v0.0.0,它将可以正常工作。

现在,执行代码时,您将拥有

Hello from Bar
Hello from Foo

Here is GitHub link for this code example

答案 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/中的文件夹代表您要构建的二进制文件数。

for more

答案 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?

相关问题