如何使用"内部"包?

Tim*_*nov 23 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"
)
Run Code Online (Sandbox Code Playgroud)

project/在GOPATH树外面.无论我尝试从main.go无效的任何方式导入的路径,唯一正常工作的情况是import "./internal/foo|bar".我认为我做错了什么或者一般来说"内部"包装的想法是错误的.请问有人能让事情更清楚吗?

UPDATE

上面的例子是正确的,我唯一需要的是放置project/文件夹$GOPATH/src.所以事情是导入路径,project/internal/foo|bar如果我们只从project/子树而不是从外部导入它是可行的.

Vla*_*den 23

在 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
Run Code Online (Sandbox Code Playgroud)

项目/内部/酒吧/go.mod

module bar

go 1.14
Run Code Online (Sandbox Code Playgroud)

项目/内部/bar/bar.go

package bar

import "fmt"

//Bar prints "Hello from Bar"
func Bar() {
    fmt.Println("Hello from Bar")
}

Run Code Online (Sandbox Code Playgroud)

项目/内部/foo/go.mod

module foo

go 1.14
Run Code Online (Sandbox Code Playgroud)

项目/内部/foo/foo.go

package foo

import "fmt"

//Foo prints "Hello from Foo"
func Foo() {
    fmt.Println("Hello from Foo")
}
Run Code Online (Sandbox Code Playgroud)

项目/main.go

package main

import (
    "internal/bar"
    "internal/foo"
)

func main() {
    bar.Bar()
    foo.Foo()
}

Run Code Online (Sandbox Code Playgroud)

现在最重要的模块 project/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

Run Code Online (Sandbox Code Playgroud)

这里有几件事:

  1. 您可以在 require 中使用任何名称。如果您愿意,您可以拥有项目/内部/酒吧。Go 认为它是包的 URL 地址,因此它会尝试从网络中提取它并给您错误
go: internal/bar@v1.0.0: malformed module path "internal/bar": missing dot in first path element
Run Code Online (Sandbox Code Playgroud)

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

replace internal/bar => ./internal/bar
Run Code Online (Sandbox Code Playgroud)
  1. 在这种情况下,版本无关紧要。你可以有 v0.0.0,它会工作。

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

Hello from Bar
Hello from Foo
Run Code Online (Sandbox Code Playgroud)

这是此代码示例的 GitHub 链接

  • 为什么要为每个包使用一个模块?我的意思是,我明白为什么您不会在整个项目中使用单个“go.mod”,毕竟,您希望引用您的模块的项目无法访问“internal”文件夹(不要在这方面引用我) ,我认为内部无法访问,但我还没有测试过)。但是为“foo”和“bar”使用不同的“go.mod”文件似乎很奇怪,为什么不应该为整个“internal”目录使用单个“go.mod”文件呢? (10认同)

eva*_*nal 16

包裹必须位于您的包装$GOPATH中才能进口.您提供的示例import "./internal/foo|bar"有效,因为它执行本地导入.internal只有这样,不共享目录的公共根目录的代码internal才能导入其中的包internal.

如果你把这一切都在你的GOPATH然后试图从不同的位置就像导入OuterFolder/project2/main.go其中,OuterFolder既包含projectproject2随后import "../../project/internal/foo"将失败.import "foo"由于不满足这个条件,它也会因为你尝试过的任何其他方式而失败;

如果导入代码位于以"internal"目录的父级为根的树之外,则不允许导入包含元素"internal"的路径.

现在,如果你有路径$GOPATH/src/project,那么你可以做的import "foo",并import "bar"从内部$GOPATH/src/project/main.go和进口会成功.project然而,未包含在下面的东西将无法导入foobar.


Edw*_*kwu 8

以下方式更具可扩展性,尤其是当您计划构建多个二进制文件时

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/
Run Code Online (Sandbox Code Playgroud)

inside里面的文件夹cmd/代表你想要构建的二进制文件的数量.

更多