下一版本 Go 在最基础的 Go 软件包方面将包含显著的 API 变更。除非进行更新,采用新版 API,否则实现 HTTP 服务器句柄、调用 net.Dial、调用 os.Open 及使用 reflect 软件包的代码将无法编译。随着 Go 语言愈加稳定,发布频率减缓将成为常态。每周的快照版本中都会有 API 变更,部分可能能自动管理;然而,合计起来看,手动更新现有代码的工作量仍然非常得大。 Gofix 是一款减轻更新现有代码工作量的新工具。它读取源文件中的程序,查找对旧版 API 的使用,用当前 API 进行改写,并将程序写回文件。有些 API 变更未保持全部原有功能,所以 gofix 的改写有时并不完美。当无法自动改写旧版 API 时,gofix 将给出警告及文件名与所在行数,开发者可以检查并亲自重写代码。Gofix 负责处理乏味冗长的简单变更,而把真正应该注意的部分留给开发者处理。 每当 API有明显变更时 gofix 的代码都会得到更新,尽可能进行自动转换。如果用户更新到新版 Go 后代码无法编译,只需在源码目录运行一下 gofix。 例如,gofix 可以将出自 fmt/print.go 的这段代码: switch f := value.(type) { case *reflect.BoolValue: p.fmtBool(f.Get(), verb, field) case *reflect.IntValue: p.fmtInt64(f.Get(), verb, field) // ... case reflect.ArrayOrSliceValue: // Byte slices are special. if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 { // ... } // ... } 重写为采用新版 API 的代码: switch f := value; f.Kind() { case reflect.Bool: p.fmtBool(f.Bool(), verb, field) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.fmtInt64(f.Int(), verb, field) // ... case reflect.Array, reflect.Slice: // Byte slices are special. if f.Type().Elem().Kind() == reflect.Uint8 { // ... } // ... } 几乎每行都有微小变更。重写的变更量大,但几乎完全机械化,可以交付计算机处理。 Gofix 的实现离不开 Go 标准库中对将 Go 源文件解析为语法树与将语法树打印回为 Go 源代码的 支持。 此外,Go 打印库能以官方格式(通常可以通过 gofmt 工具实现)打印程序,可让 gofix 对 Go 程序的机械性更改不造成谬误。实际上,设计 gofmt 的关键动机之一——或许仅次于避免特定花括号放置位置的争论——就是简化重写 Go 程序的工具,如 gofix 的设计。 Gofix 注定成为不可或缺的工具。Go 团队一直使用该工具更新他们自己的项目,甚至是更新 Google 内部的源码树。Gofix 允许用户修复错误或重构包 API 而无须担心转换现有代码的开销。用户也可以对 gofix 进行扩展,来支持他们自己的 API。 |