【问题标题】:What is the purpose of the package declaration?包装声明的目的是什么?
【发布时间】:2016-11-28 13:45:57
【问题描述】:

每个 Go 文件都以 package <something> 开头。

据我所知——这可能是我遗漏了一些信息的地方——<something> 只有两个可能的值:它所在的目录的名称*,或main。如果是main,则该目录中的所有其他文件也只能有main。如果是其他原因,则项目不一致/违反约定。

现在如果是目录名,那就是多余的,因为同样的信息在目录名中。

如果是main,这有点没用,因为据我所知,没有办法告诉go build“请构建所有 main包”。

* 因为,换句话说,一个目录就是一个包。

【问题讨论】:

  • 如果您需要从main访问项目中另一个文件中定义的某些功能或结构或接口怎么办?它还有助于遵循模块化方法来构建项目。
  • @ameyCU 那我就用它。来自同一包(= 目录)中所有文件的所有标识符都在一个范围内。
  • 当你开始制作/导入一个包以在你的主包中使用它时。
  • 其实有一种方法可以在 GOPATH 子树的某个根目录下构建所有主包,例如: go install -v github.com/foo/bar/... 将构建并安装所有对应于 GOPATH/src/github.com/foo/bar 下的 'package main' 包的可执行文件

标签: go


【解决方案1】:

包名不必与目录名一致。在xyz/go-foobar 目录中可以有package foobar。在这种情况下,xyz/go-foobar 变成了一个导入路径,但您用来质量标识符(函数、类型等)的 包名称 将是 foobar

这是一个更具体的示例:我创建了一个测试包http://godoc.org/github.com/dmitris/go-foobarhttps://github.com/dmitris/go-foobar 中的源代码)-您可以从文档页面中看到,导入路径是“github.com/dmitris/go- foob​​ar”,但包名是foobar,所以你可以将它提供的函数称为foobar.Demo()(而不是go-foobar.Demo())。

一个类似的现实示例 - NSQ Messaging 平台的导入路径是“github.com/nsqio/go-nsq”,而包名称是“nsq”:http://godoc.org/github.com/nsqio/go-nsq。但是,为了用户友好和简单起见,标准和推荐的做法是尽可能保持导入路径的最后部分和包名称相同。

package main 不是没用的——它告诉 Go 编译器创建一个可执行文件而不是 .a 库文件(go installgo getgo build 丢弃编译结果)。可执行文件以放置package main 文件的目录名称命名。再举一个具体的例子——我做了一个测试程序https://github.com/dmitris/go-foobar-client,你用go get github.com/dmitris/go-foobar-client 安装它,你应该得到一个go-foobar-client 可执行文件放在你的$GOPATH/bin 目录中。 Go 编译器从放置 package main 文件的目录名称中获取可执行文件的名称。包含main() 函数的.go 文件的文件名并不重要——在上面的示例中,我们可以将main.go 重命名为client.go 或其他名称,但只要封闭目录称为go-foobar-client ,这就是生成的可执行文件的命名方式。

要获得更多关于 Go 包的可访问且实用的阅读材料,我推荐 Dave Cheney 的文章“建立 Go 项目的五个建议”http://dave.cheney.net/2014/12/01/five-suggestions-for-setting-up-a-go-project

【讨论】:

  • "package main 不是没用的——它告诉 Go 编译器创建一个可执行文件而不是 .a 库文件。"嗯,我无法重现。当我的包被称为 main 以外的东西时,go build 绝对什么都不做。
  • @AndreKR 见:What does go build build?
  • 感谢@icza - 编辑了答案以提及“go install”和“go build”在这方面的区别。 “go build”保留已编译库的“边缘”情况是当您使用“-work”选项运行它时,例如。 go build -x -work - 编译后的 .a 可以在 WORK 目录下找到,这个选项不会被删除。
【解决方案2】:

您“拥有”的缺失信息是包名称不需要与目录名称相同。

使用文件夹名称以外的包名称是完全可以的。如果这样做,您仍然需要根据目录结构导入包,但在导入之后,您必须使用您在包子句中使用的名称来引用它。

例如,如果您有一个文件夹$GOPATH/src/mypck,并且其中有一个文件a.go

package apple

const Pi = 3.14

使用这个包:

package main

import (
    "mypck"
    "fmt"
)

func main() {
    fmt.Println(apple.Pi)
}

就像您可以使用相对导入但不建议使用一样,您可以使用包含文件夹以外的包名称,但这也是不可取的,以避免进一步的误解。

请注意,规范甚至不要求属于同一包的所有文件都位于同一文件夹中(但这可能是实现要求)。 Spec: Package clause:

一组文件共享相同的 PackageName 形成一个包的实现。实现可能要求包的所有源文件位于同一目录中。

这个有什么用?

简单。包名是 Go identifier:

identifier = letter { letter | unicode_digit } .

允许在标识符中使用 unicode 字母,例如αβ 是 Go 中的有效标识符。文件夹和文件名不是由 Go 处理,而是由操作系统处理,不同的文件系统有不同的限制。实际上有许多文件系统不允许所有有效的 Go 标识符作为文件夹名称,因此您将无法命名您的包,否则语言规范会允许。

因此,一方面并非所有有效的 Go 标识符都可能是有效的文件夹名称。另一方面,并​​非所有有效的文件夹名称都是有效的 Go 标识符,例如 go-math 在大多数(所有?)文件系统中是有效的文件夹名称,但它不是有效的 Go 标识符(因为标识符不能包含破折号 @ 987654330@ 字符)。

可以选择使用与其包含文件夹不同的包名称,您可以选择按照语言规范允许的方式真正命名您的包,而不管底层操作系统和文件系统如何,并将其放在一个文件夹中底层操作系统和文件系统允许 - 无论包名如何。

【讨论】:

  • 有趣。你有一个现实生活中的例子(Github?),它被用来带来一些好处吗?
  • 关于规范:我认为我们不应该对 Go 语言和 Go 实现挑剔,Go 语言只有一个相关的实现。 :)
  • @AndreKR 它使您可以选择使用底层操作系统或文件系统可能不允许的包名称。请参阅编辑后的答案。
  • 我希望我能接受这两个答案。我会赞成这个并接受另一个,因为这更接近问题,尤其是在您发表评论之后。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
  • 2019-03-02
  • 2012-07-13
  • 2013-05-17
  • 1970-01-01
  • 2010-12-20
  • 2017-07-12
相关资源
最近更新 更多