Golang API版本文档指南

Golang API版本文档指南 在像 Java / C# 这样的语言中,我们有针对新引入的 API / 函数的 API 版本控制。

然而,除了变更日志之外,我没有找到任何与 API 相关的文档。

例如,在 time 包中,Go 1.17 引入了一个新方法 UnixMili()。但在 API 方法中,我找不到任何文档说明 UnixMili() API 是何时或从哪个 Go 版本开始添加的。

我认为需要这类文档来理解标准库的变化。

17 回复

我了解这一点。请参考我之前的评论。

这并不是我试图提出的建议,而是Go版本的“内联代码级别”文档。您展示的那个是取自API变更日志文件。

更多关于Golang API版本文档指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


明白了。正如我所说,最好的选择可能是去联系 golang-nuts 邮件列表。我个人认为,文档中的版本信息已经足够了——可以看我上次更新中的截图。使用标准库的 API 并不需要去阅读源代码。

NobbZ:

无论如何,在网站上浏览文档仍然要容易得多。

Godoc(不是 go doc)具有 Web 模式:

godoc -http=localhost:6060

这会有帮助吗?

正如我所说,这令人遗憾但却是事实,确实有些开发者在这方面存在问题。可能是因为他们忘记事先启动服务器,也可能是因为 localhost 已不再是标准的书签地址。

不过,我完全理解这个论坛并非讨论或提议此事的合适场所,但或许这里会有人支持一项提议?

我的主要观点是,为什么在源代码文档本身(比如这个方法 go/time.go at master · golang/go · GitHub)添加此方法时,我无法看到对应的 Go 版本信息?

是什么阻止了我们在方法文档中看到 Go 版本?

在 pkg.go.dev 上还有另一个技巧可以使用。

https://pkg.go.dev/time@go1.18beta1?tab=versions

当 API 发生变化时,会有一个差异对比(默认不展开)。你可以点击“展开全部”来查看 API/数据结构的变化。

需要说明的是,这里并非官方的 Go 论坛,活跃在此的大多数人都是像你我一样的 Go 用户。如果你想联系 Go 的维护者,Google 群组 golang-nuts 或许是个不错的起点。

关于你的请求,你为什么需要知道这个?

我之所以这样问,是因为在代码中需要变更日志信息可能是一个 XY 问题

我认为 @christophberger 关于 Elixir、Rust 和 Go,甚至 Java 之间文档比较的观点,@NobbZ 说得有道理。

这个帖子的初衷是在意识到从 Go 1.15 升级到 Go 1.17 时 Go 发生了变化之后提出的。 是的!你说得对,你可以在网上通过 godoc 找到文档,但如果有人直接阅读源代码,这还不够。

我认为这不是XY问题,因为其他语言也能毫无问题地实现这一点(个人观点)。

长远来看,Go语言会添加更多函数/弃用某些功能,目前相关列表维护在以下位置:go/api at master · golang/go · GitHub

然而,随着列表越来越大,你可能需要在函数注释文档中添加Go版本信息,原因有二:

  1. 明确你对函数变更的意图,并跟踪历史记录。
  2. 浏览代码时便于查找,且非常直观。

我仍然不明白这项要求的目的是什么。

  1. “明确你对函数变更的意图”——我不明白代码文档为何是讨论意图的合适场所?意图应该在提案、草案和问题中进行讨论。

  2. “追踪历史”——同意,但为什么要在代码文档里进行?为什么变更日志不够用?在编码时,哪种开发需求需要你查找历史变更?

  3. “在浏览代码时易于查找且非常直接”——再次提问,你什么时候会需要这个?

存在什么现实世界的问题,使得在代码文档中记录代码变更历史成为必需?

是的,无论怎样我都必须安装它,但在网站上浏览文档要容易得多,而且说实话,我的一些同事明白他们需要针对 Go 1.10 版本,却不明白他们应该在终端上使用 go doc 来阅读文档。他们更不理解可以运行一个本地文档服务器。他们为官方文档设置了书签,但那些文档缺少关键信息。

另外,在终端上使用 go doc 的问题是,你需要事先知道函数名称,无法像在网站上那样搜索 API。

我们想从官方文档中得到解答的主要问题是:在我必须使用的版本中,这个有文档记录的 API 片段是否可用?

@Rheza

标准库的更新会随着新 Go 版本的发布同步进行。因此,所有对标准库的更新都记录在 Go 发布说明 中。

顺便提一下:如果你需要知道某个功能是何时添加到标准库的,你可能是因为某个原因而停留在旧的 Go 版本上——这或许是不必要的。Go 提供了 Go 1 兼容性承诺,旨在让跟上最新的 Go 工具链变得尽可能简单。简而言之:旧的 Go 代码应该能在最新的 Go 1.x 版本中不加修改地运行,除了极少数例外情况,例如涉及安全修复的重大变更。

作为一名经常使用 Elixir 和 Python 以及 Go 的开发者,并且由于客户要求或强制规定等原因,常常被迫使用某些旧的编译器或运行时,我了解 Elixir 和 Python 如何处理新增或变更的 API,也了解 Go 的处理方式,我完全支持这个请求!

Elixir 有文档元数据,在函数/宏的头部有清晰的 since x.y.z 标记,表明函数和宏从哪个版本开始可用。Python 通常也有关于方法历史的清晰段落,可惜常常隐藏在叙述性文字中,但仍然是可用的。

对于 Go,要知道某个方法/类型/函数/接口在特定 Go 版本中是否可用的唯一方法是:安装该版本并使用 go doc 命令,或者在本地拥有与该版本匹配的预渲染 HTML 文档。

Rust(我经常与 Elixir 一起使用)拥有与 Go 相似甚至更严格的兼容性保证,即便如此,它们也会清晰地标注一个函数/方法等是否仅从某个特定版本开始添加。

感谢 @Rheza@NobbZ 帮助我理解这个请求的背景。

恐怕除了建议使用 godoc -http 之外,我帮不上什么忙,所以也许你可以联系 Go 团队。

作为开始,以下是我在 GitHub 上的 Go 仓库镜像中发现的两个问题:

x/tools/cmd/godoc: 显示 Go 在哪个版本引入了符号 · Issue #5778 · golang/go

以及为 cmd/doc 实现该功能的后续问题:

cmd/doc: 显示 Go 在哪个版本引入了符号 · Issue #29204 · golang/go

有趣的是:这些信息似乎已经存在于在线文档中,例如在 Go 1.17 中新增了几个函数的 unsafe 包。

Screen Shot 2022-01-24 at 14.58.11

我知道我们有更新日志和 Go 文档 time package - time - Go Packages

但我想更深入地了解,为什么我们不在代码文档本身添加有用的信息 go/src/time/time.go at master · golang/go · GitHub

这样可以避免关于已更改函数的杂乱文档。例如(取自发布文档):

go1.17.2(发布于 2021-10-07)包含一个链接器和 misc/wasm 目录的安全修复,以及对编译器、运行时、go 命令以及 time 和 text/template 包的 bug 修复。详情请参阅我们问题跟踪器上的 Go 1.17.2 里程碑。

要解释新增/更改的方法/函数,这本身并不直观。

通常当我编码时,我会直接查看代码文档本身,而不是更新日志。我相信大多数开发者也是这样做的。

我的观点是,如果您/Go 维护者可以考虑在代码注释中添加此方法是(自何时)添加的,这将有助于其他人理解这些变更。

我不会为我的提议邀功。决定权在您。

感谢 @NobbZ。我确实很好奇:在什么情况下,已安装版本的 go doc 还不够用?什么时候需要在实际项目之外查询标准库的历史变更记录?

如果你需要保持代码与某个旧版 Go 1.x 版本兼容,那么我认为除了实际使用那个特定的 Go 1.x 版本来开发该代码之外别无他法。否则,尽管有兼容性承诺,你的代码仍可能遇到一些细微的错误,这些错误在该版本中存在,但在当前工具链中不存在。

示例:Go 调度器在 Go 1.14 中变成了抢占式。相同的代码在使用 Go 1.14 或更高版本编译时,与使用更早版本编译时的行为不同。好吧,这不是标准库 API 的问题,但我的观点是,如果代码需要在历史的 Go 版本上运行,就需要使用该版本进行开发和测试。


编辑补充:但我也意识到,我可能完全忽略了对处于类似你这种情况的其他人来说非常明显的一点。如果缺少标准库 API 的版本信息确实对日常开发构成了严重障碍(而不是一个寻找问题的解决方案),我建议在 golang-nuts - Google Groups 上讨论这个请求——你应该能在那里找到一些接近甚至属于 Go 核心团队的人。

在Go语言中,标准库的API版本控制确实不像Java/C#那样有显式的版本标注,但可以通过以下几种方式获取API引入版本信息:

1. 官方文档中的"Added in version"标注

虽然Go文档没有强制要求,但部分新API会标注版本信息。例如time.UnixMilli()的文档中实际包含了版本提示:

// UnixMilli returns the local Time corresponding to the given Unix time,
// msec milliseconds since January 1, 1970 UTC.
//
// To extract the millisecond component from a Time t, use t.UnixMilli().
//
// This function was added in Go 1.17.
func (t Time) UnixMilli() int64 {
    return t.unixSec()*1e3 + int64(t.nsec())/1e6
}

2. 使用go/doc API获取版本信息

可以通过编程方式检查API的可用性:

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
    "strings"
)

func checkAPIVersion(pkgPath, funcName string) {
    fset := token.NewFileSet()
    pkgs, err := parser.ParseDir(fset, pkgPath, nil, parser.ParseComments)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    for _, pkg := range pkgs {
        docPkg := doc.New(pkg, "./", doc.AllDecls)
        for _, t := range docPkg.Types {
            for _, m := range t.Methods {
                if m.Name == funcName {
                    fmt.Printf("Found %s in type %s\n", funcName, t.Name)
                    if strings.Contains(m.Doc, "Go 1.") {
                        fmt.Printf("Version hint: %s\n", m.Doc)
                    }
                }
            }
        }
    }
}

func main() {
    checkAPIVersion("/usr/local/go/src/time", "UnixMilli")
}

3. 查看Go发行说明和变更日志

最可靠的方式是查阅官方发布文档:

# 查看特定版本的发布说明
go tool dist list -json | grep -A5 -B5 "1.17"

# 或直接访问在线文档
# https://golang.org/doc/go1.17#time

4. 使用godoc的本地版本

运行本地godoc服务器查看历史版本:

# 启动godoc服务器
godoc -http=:6060

# 然后访问 http://localhost:6060/pkg/time/
# 查看特定版本的文档需要切换Go版本

5. 通过构建标签检查可用性

在实际代码中可以通过构建标签处理版本差异:

//go:build go1.17
// +build go1.17

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    // UnixMilli只在Go 1.17+可用
    ms := t.UnixMilli()
    fmt.Printf("Unix milliseconds: %d\n", ms)
}

6. 使用api工具检查

Go工具链中的api命令可以比较API变化:

# 比较两个版本的API差异
go tool api -diff go1.16.txt go1.17.txt | grep -i unixmilli

# 输出会显示新增的API
# pkg time, method (Time) UnixMilli() int64

虽然Go没有在文档中强制要求版本标注,但通过上述方法可以有效确定API的引入版本。对于标准库,最权威的来源始终是Go官方发布说明和源码仓库的变更历史。

回到顶部