【问题标题】:In Go does the package name have to be identical to inner-most directory name?在 Go 中,包名是否必须与最里面的目录名相同?
【发布时间】:2020-10-20 12:33:01
【问题描述】:

注意这个问题是关于 Go 语言规范而不是最佳实践或建议。

看了很多关于包的文章,还是不太明白目录和包名的关系。这是我的例子。

我的项目结构如下图所示。当我执行go run ~/go/src/myproj/main.go 时,错误提示:

src\myproj\main.go:5:2: 在以下任何一个中都找不到包“myproj/pa/pb”: c:\go\src\myproj\pa\pb(来自 $GOROOT) C:\Users\terry\go\src\myproj\pa\pb(来自 $GOPATH)

但是,如果我在 p.go 中将 package pb 更改为 package pa,并将导入从 "myproj/pa/pb" 更改为 "myproj/pa",并在 main.go 中将 fmt.Print(pb.Greet) 更改为 fmt.Print(pa.Greet),它将起作用。最里面的目录是否必须与包声明名称匹配?我的 go 版本是 1.14.4

【问题讨论】:

  • "最里面的目录是否必须与包声明名称匹配?"不,当然不。但这是一个非常 明智 约定,你永远不应该打破它,因为如果你这样做,人们会(理所当然地)恨你。

标签: go


【解决方案1】:

在 go 中,一个约定是包的名称应该与其源目录的名称相同。

这里是Effective Go Blog的摘录:

另一个约定是包名是其源目录的基名; src/encoding/base64 中的包被导入为“encoding/base64”,但名称为 base64,不是 encoding_base64,也不是 encodingBase64。

这意味着一个目录只包含一个包。它在围棋中非常基础,您可以将其视为固定规则,尽管从技术上讲,它只是一个约定。

【讨论】:

    【解决方案2】:

    经过一些试验和错误,我发现了发生了什么。 包名必须与最里面的目录名相同?没有。

    main.go 中只需执行以下操作即可。

    package main
    
    import (
        "fmt"
        "myproj/pa"
    )
    
    func main() {
        fmt.Print(pb.Greet)
    }
    

    我们也可以给它一个别名,如下所示。

    package main
    
    import (
        "fmt"
        pc "myproj/pa"
    )
    
    func main() {
        fmt.Print(pc.Greet)
    }
    

    也就是说,go 中的 package 是一个带有 package xxx 声明的文件目录。目录名称在导入过程中很重要。该目录是导入路径的一部分。但是导入文件中使用的是package xxx中的xxx或者那个xxx的别名。

    当然,不建议做这种事情,但最好的做法还是不要这样做来迷惑人。

    【讨论】:

    • 技术上这是可能的......但你永远不应该这样做。
    • 这个问题是在询问 Go 规范而不是推荐或最佳实践。通过上面的例子,人们就会明白 go package 是如何工作的,当看到一些 3rd package 有这样的东西时,人们就不会再感到困惑了。
    • 我知道你想说什么,我很感激你想了解技术细节。我想说的是,在我现在使用 Go 的 5 年中,我从未见过与源目录不同的包名称。
    【解决方案3】:

    在 Go 中,包名是否必须与最内层目录名相同?

    它不必相同,但它是一个常见的约定。除非你有充分的理由,否则你不应该偏离它。

    如果你想拥有不同的包名和目录名,你可以在文件的包子句中使用 import 注释。

    package <package_name> // import "<import_path>"
    

    这很有用,您的文件结构不利于描述性名称,例如,如果在文件路径中使用多个版本。

    例如,Google 的客户端 SDK 使用如下路径:

    • 包名:slides
    • 导入路径:google.golang.org/api/slides/v1

    为了支持这一点,文件的包子句如下:

    package slides // import "google.golang.org/api/slides/v1"
    

    这样,在调用代码中就可以做到以下几点,看起来很明智。

    package main
    
    import(
        "google.golang.org/api/slides/v1"
    )
    
    func main() {
        fmt.Println(slides.PresentationsScope)
    }
    

    在此处查看更多信息:https://github.com/googleapis/google-api-go-client/blob/master/slides/v1/slides-gen.go

    在此处的示例中,您将使用:

    package pb // import "myproj/pa"
    

    如果你不使用包子句的方式,编辑器可能会自动为你的代码添加一个别名,例如:

    import pb "myprog/pa"
    

    【讨论】:

      【解决方案4】:

      包名不必是包目录的基础。 go 工具 goimports 在清理源文件的导入时,实际上会采用包名称并将其显示为导入的别名,如果它与基础不匹配 - 使导入代码看起来更有意义但别名不是必需的 - 没有它,文件将编译。查看包的 AST 时, *types.Package 有一个 Name 方法,它返回包名。当使用 golang.org/x/tools/go/packages.Load 函数加载包时,返回的 *packages.Package 有一个 Name 属性,该属性也返回包名。这在通过程序生成代码时非常有用。

      【讨论】:

        猜你喜欢
        • 2020-07-19
        • 2020-10-11
        • 2018-09-23
        • 1970-01-01
        • 1970-01-01
        • 2015-04-19
        • 2014-03-09
        • 1970-01-01
        • 2011-04-03
        相关资源
        最近更新 更多