标签 泛型 下的文章

Go 1.26 重磅更新:用 go fix 重塑代码现代化的艺术

本文永久链接 – https://tonybai.com/2026/02/19/using-go-fix-to-modernize-go-code

大家好,我是Tony Bai。

2026年2月,Go 1.26 正式发布。除了语言层面的新特性(如 new(expr))和运行时的性能提升(如 Green Tea GC)之外,工具链迎来了一次史诗级的升级:go fix 命令被彻底重写。

在过去,go fix 更多是用来解决破坏性变更的“补救工具”(例如 Go 1.4 到 Go 1.5 的迁移)。但在 Go 1.26 中,它华丽转身,成为了一个代码现代化(Modernization)的利器。它不再仅仅是修复错误,而是主动帮助你将代码升级到 Go 的最新惯用法(Idioms)。

本文将基于 Alan Donovan 的官方博文,深度解析新版 go fix 的工作原理、核心特性——Modernizers(现代化器),以及其背后的分析框架架构。旨在帮助你彻底掌握这一新工具,让你的 Go 代码库焕发新生。

背景

随着 Go 语言进入“后泛型时代”(Post-Go 1.18),语言特性的演进速度明显加快。从 strings.Cut 到 min/max 内置函数,再到 range-over-func,每一个版本都在引入更简洁、更高效的表达方式。

然而,现实是残酷的:代码库具有巨大的惯性

大多数现存的 Go 代码依然停留在几年前的写法上。更糟糕的是,随着 LLM(大语言模型)编程助手的普及,AI 正在基于海量的旧代码进行训练。这就导致了一个恶性循环:AI 学习了旧的写法,生成了旧的写法,开发者接受了旧的写法,进一步污染了语料库。

Go 团队意识到了这一点。为了打破这个循环,确保未来的模型和新加入的开发者能够掌握最新的 Go 习惯用法,Go 1.26 推出了全新的 go fix。它利用了一套复杂的静态分析算法,自动识别并重构代码,使其拥抱现代化的 Go。

go fix 的全新打开方式

新版的 go fix 在使用体验上向 go build 和 go vet 看齐。它接受标准的包模式(Package Patterns)。

1. 基础用法

要“修复”当前目录及其子目录下的所有包,只需运行:

$ go fix ./...

如果运行成功,它会静默地直接修改你的源文件。

注意:go fix 会自动忽略生成的文件(Generated Files),因为对生成文件的修复应该在生成器本身中进行,而不是在产物中。

2. 预览变更:-diff

由于 go fix 可能会瞬间修改成百上千个文件,直接运行可能让人心惊肉跳。Go 团队贴心地提供了 -diff 标志,让你在应用变更前先进行预览:

$ go fix -diff ./...
--- dir/file.go (old)
+++ dir/file.go (new)
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = after
...

因此,我们强烈建议每次升级 Go 工具链版本后,都对项目运行一次 go fix。在运行前,请确保 Git 工作区是干净的,这样你可以清晰地查看 go fix 带来的改动,并方便同事进行 Code Review。

3. 选择性执行

默认情况下,go fix 会运行所有注册的分析器。但在大型项目中,为了减轻 Code Review 的负担,你可能希望一次只应用一种类型的修复。

你可以通过 go tool fix help 查看所有可用的分析器:

$go tool fix help
fix is a tool for static analysis of Go programs.

fix examines Go source code and reports diagnostics for
suspicious constructs or opportunities for improvement.
Diagnostics may include suggested fixes.

An example of a suspicious construct is a Printf call whose arguments
do not align with the format string. Analyzers may use heuristics that
do not guarantee all reports are genuine problems, but can find
mistakes not caught by the compiler.

An example of an opportunity for improvement is a loop over
strings.Split(doc, "\n"), which may be replaced by a loop over the
strings.SplitSeq iterator, avoiding an array allocation.
Diagnostics in such cases may report non-problems,
but should carry fixes that may be safely applied.

For analyzers of the first kind, use "go vet -vettool=PROGRAM"
to run the tool and report diagnostics.

For analyzers of the second kind, use "go fix -fixtool=PROGRAM"
to run the tool and apply the fixes it suggests.

Registered analyzers:

    any          replace interface{} with any
    buildtag     check //go:build and // +build directives
    fmtappendf   replace []byte(fmt.Sprintf) with fmt.Appendf
    forvar       remove redundant re-declaration of loop variables
    hostport     check format of addresses passed to net.Dial
    inline       apply fixes based on 'go:fix inline' comment directives
    mapsloop     replace explicit loops over maps with calls to maps package
    minmax       replace if/else statements with calls to min or max
    newexpr      simplify code by using go1.26's new(expr)
    omitzero     suggest replacing omitempty with omitzero for struct fields
    plusbuild    remove obsolete //+build comments
    rangeint     replace 3-clause for loops with for-range over integers
    reflecttypefor replace reflect.TypeOf(x) with TypeFor[T]()
    slicescontains replace loops with slices.Contains or slices.ContainsFunc
    slicessort   replace sort.Slice with slices.Sort for basic types
    stditerators use iterators instead of Len/At-style APIs
    stringsbuilder replace += with strings.Builder
    stringscut   replace strings.Index etc. with strings.Cut
    stringscutprefix replace HasPrefix/TrimPrefix with CutPrefix
    stringsseq   replace ranging over Split/Fields with SplitSeq/FieldsSeq
    testingcontext replace context.WithCancel with t.Context in tests
    waitgroup    replace wg.Add(1)/go/wg.Done() with wg.Go

By default all analyzers are run.
... ...

要查看特定分析器的文档:

$ go tool fix help forvar
forvar: remove redundant re-declaration of loop variables

The forvar analyzer removes unnecessary shadowing of loop variables.
Before Go 1.22, it was common to write for _, x := range s { x := x ... }
to create a fresh variable for each iteration. Go 1.22 changed the semantics
of for loops, making this pattern redundant. This analyzer removes the
unnecessary x := x statement.

This fix only applies to range loops.

要单独运行某个分析器(例如 any),可以使用对应的标志:

$ go fix -any ./...

反之,如果你想运行除了 any 之外的所有分析器,可以将其禁用:

$ go fix -any=false ./...

4. 交叉平台修复

和 go vet 一样,go fix 也是基于特定的构建配置(Build Configuration)进行分析的。如果你的项目包含大量特定于平台的文件(例如 _linux.go, _windows.go),建议针对不同的 GOOS 和 GOARCH 多次运行:

$ GOOS=linux   GOARCH=amd64 go fix ./...
$ GOOS=darwin  GOARCH=arm64 go fix ./...
$ GOOS=windows GOARCH=amd64 go fix ./...

核心特性:Modernizers(现代化器)

Go 1.26 引入了一个新概念:Modernizers。它们是一组特殊的分析器,专门用于将旧的习惯用法替换为利用新语言特性或新标准库 API 的写法。

以下是几个最具代表性的 Modernizers 示例,展示了它们如何简化代码:

1. minmax:拥抱内置函数

在 Go 1.21 之前,计算最小值/最大值通常需要写冗长的 if/else 语句。

旧代码:

x := f()
if x < 0 {
    x = 0
}
if x > 100 {
    x = 100
}

minmax 修复后可能的样子:

x := min(max(f(), 0), 100)

代码意图一目了然,且消除了分支跳转,可能带来微小的性能提升。

2. rangeint:告别 C 风格循环

Go 1.22 引入了对整数的 range 支持。

旧代码:

for i := 0; i < n; i++ {
    f()
}

rangeint 修复后:

for range n {
    f()
}

如果你不需要索引 i,新的写法极其清爽。

3. stringscut:字符串分割的最佳实践

Go 1.18 引入的 strings.Cut 是处理“按分隔符切分”场景的神器,它比 Index + Slicing 更高效且不易出错。

旧代码:

i := strings.Index(s, ":")
if i >= 0 {
    return s[:i]
}

stringscut 修复后:

before, _, ok := strings.Cut(s, ":")
if ok {
    return before
}

4. newexpr:Go 1.26 的专属语法糖

这是 Go 1.26 刚刚引入的语言变动:new() 函数现在支持传入表达式,直接初始化变量。这在处理 Protobuf 或 JSON 的可选字段(Pointer 类型)时非常有用。

旧代码(通常需要辅助函数):

func newInt(x int) *int { return &x }

data, err := json.Marshal(&RequestJSON{
    URL: url,
    Attempts: newInt(10), // 需要定义辅助函数或临时变量
})

newexpr 修复后:

data, err := json.Marshal(&RequestJSON{
    URL: url,
    Attempts: new(10), // Go 1.26 原生支持!
})

newexpr 这样的 Modernizer 非常智能。它会检查你的 go.mod 文件中的 go 指令或文件的 //go:build 标签。只有当你的项目明确声明支持 Go 1.26 或更高版本时,它才会建议由于 new(expr) 带来的修改。这确保了 go fix 不会引入破坏向后兼容性的代码。

协同效应与冲突解决

go fix 的强大之处在于它是迭代式的。应用一个修复可能会触发另一个修复。

协同效应(Synergy)示例

考虑一个经典的性能陷阱:在循环中拼接字符串。

初始代码:

s := ""
for _, b := range bytes {
    s += fmt.Sprintf("%02x", b) // O(N^2) 复杂度!
}
use(s)

第一轮 go fix (stringsbuilder):

分析器识别出这是低效的字符串拼接,将其重构为 strings.Builder。

var s strings.Builder
for _, b := range bytes {
    s.WriteString(fmt.Sprintf("%02x", b))
}
use(s.String())

第二轮 go fix (fmtappendf):

一旦代码变成了 WriteString(Sprintf(…)),另一个分析器(源自 staticcheck 的 QF1012)就会识别出这可以优化为 fmt.Fprintf,不仅更简洁,而且直接写入 Buffer,减少了中间内存分配。

var s strings.Builder
for _, b := range bytes {
    fmt.Fprintf(&s, "%02x", b)
}
use(s.String())

因此,对于大型重构,建议运行多次 go fix,直到代码达到稳定态(Fixed Point)。

冲突处理

go fix 可能会在同一文件的不同位置应用几十个修复。它内部使用了一个简单的三路合并算法(Three-way Merge)来协调这些修改。如果两个修复在语法上冲突(例如修改了同一行),工具会丢弃其中一个,并提示用户重新运行。

但还有一种更棘手的语义冲突(Semantic Conflict)。

例如,修复 A 删除了变量 x 的一次使用,修复 B 删除了 x 的另一次使用。两个修复单独看都没问题,但合在一起后,变量 x 变成了“未使用的变量”,导致编译错误。

go fix 的解决方案很务实:它在所有修复应用完毕后,会运行一个最终的清理 Pass,自动删除那些因重构而变得多余的 import 语句。对于未使用的变量,通常会留给编译器报错,由开发者手动删除(或者等待未来的 deadcode 消除器)。

幕后英雄:Go 分析框架 (The Analysis Framework)

新版 go fix 的核心动力来自于 Go Analysis Framework

历史沿革

早在 2017 年,Go 团队将 go vet 的核心逻辑拆分成了两部分:

  1. Analyzers(分析器):纯粹的算法逻辑,负责发现问题(Checker)或建议修复(Fixer)。
  2. Drivers(驱动器):负责加载程序、运行分析器并展示结果。

这种分离架构带来了极大的灵活性。同一个分析器(比如 printf 检查)可以运行在多种场景下:

  • unitchecker:go vet 和 go fix 的底层驱动,支持增量构建。
  • gopls:Go 语言服务器,在编辑器中实时提供红色波浪线和快速修复(Quick Fix)。
  • nogo:用于 Bazel 等构建系统的驱动。
  • analysistest:用于测试分析器本身的框架。

Go 1.26 的里程碑意义在于:go fix 和 go vet 在底层实现上终于完全统一了。 它们的区别仅在于目标:vet 侧重于报告错误(低误报率),fix 侧重于自动修改(无回退,保全正确性)。

性能黑科技

为了让 go fix 能在大型代码库上秒级运行,Go 团队引入了多项基础设施优化:

  1. Inspector 与 Cursor
    分析器通常需要遍历语法树(AST)。inspector 包预先计算了遍历索引,使得分析器可以快速跳过不关心的节点。新增的 Cursor 类型更是允许在 AST 上进行类似 DOM 的灵活导航(父节点、兄弟节点)。

  2. Facts(事实)与跨包推断
    分析框架支持跨包的“事实”传递。例如,printf 检查器可以分析 log.Printf 的函数体,得出一个“Fact”:log.Printf 是 fmt.Printf 的包装器。这个 Fact 会被序列化并传递给导入了 log 包的其他包,从而实现跨包的格式化字符串检查。

  3. TypeIndex(类型索引)
    很多分析器需要查找“所有对 fmt.Printf 的调用”。与其遍历整个 AST,typeindex 预先构建了符号引用索引。这使得查找特定符号的开销从“与代码量成正比”降低为“与调用次数成正比”,对于查找冷门符号(如 net.Dial)的分析器,性能提升可达 1000 倍

未来展望:“自助式”分析 (Self-Service)

Alan Donovan 在博文中提出了一个令人兴奋的愿景:Self-Service Paradigm(自助式范式)

目前的 Modernizers 大多是针对 Go 标准库的。但第三方库的作者呢?如果你维护了一个流行的 ORM 或 Web 框架,当你升级 API 时,如何帮助你的用户自动迁移?

你不可能把你的迁移逻辑塞进 Go 官方的 go fix 里。

Go 1.26 迈出了“自助服务”的第一步:基于注解的内联器(Annotation-driven Inliner)

//go:fix inline

库作者可以在即将废弃的函数上添加一行特殊的注释:

// Deprecated: Use Pow(x, 2) instead.
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

当用户运行 go fix 时,分析器会识别这个指令,并自动将用户代码中的 Square(x) 替换为 Pow(x, 2)。

未来的可能性

  1. 动态加载分析器
    未来,Go 可能会支持从模块源代码树中动态加载分析器并安全执行。这意味着 sql 包可以自带一个检查器来防止 SQL 注入,或者你的公司内部框架可以自带一套 go fix 规则来强制执行内部编码规范。

  2. 声明式控制流检查
    许多检查逻辑都遵循“做完 Y 之后别忘了 X”的模式(例如:打开文件后别忘了 Close,获取锁后别忘了 Unlock)。Go 团队计划探索一种通用的方式,让开发者只需简单的注解就能定义这种检查,而无需编写复杂的 Go 代码来分析控制流。

小结

Go 1.26 的 go fix 不仅仅是一个工具的更新,它代表了 Go 工程化能力的一次跃迁。

它告诉我们:维护代码不仅是修修补补,更是持续的进化。 通过将最佳实践固化为代码(Analyzers),并赋予工具自动执行的能力(Fixers),Go 正在构建一个更加健康、更具韧性的生态系统。

对于每一位 Gopher 来说,现在的任务很简单:升级到 Go 1.26(记得将go.mod的go版本升级为go 1.26.0或后续版本),在你的项目中运行 go fix ./…,然后享受代码变得更现代、更高效的快感吧。

参考资料:https://go.dev/blog/gofix


你的“现代化”阻碍是什么?

自动重构工具虽然强大,但老代码库的惯性依然巨大。在你目前的项目中,有哪些“旧习惯”最让你难以割舍?你是否尝试过用 go fix 来升级你的代码?

欢迎在评论区分享你的重构经历或对新工具的看法!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

沉睡 8 年的提案被唤醒:Go 语言真的要引入“不可变类型”了吗?

本文永久链接 – https://tonybai.com/2026/02/09/go-immutable-types-8-year-dormant-proposal-awakened

大家好,我是Tony Bai。

2026 年 2 月 4 日,在 Go 语言规范团队的最新一次“语言变更评审会议”纪要中,一个尘封已久的 Issue 赫然在列:proposal: spec: immutable type qualifier #27975

这个提案最初提交于 2018 年,那是“Towards Go 2”口号喊得最响亮的年代。当时的 Go 社区正沉浸在对泛型、错误处理和不可变性的热烈讨论中。然而,随着泛型的落地,关于不可变性的声音似乎逐渐微弱。

如今,这个提案被重新摆上台面,是否意味着 Go 语言在完成泛型这一宏大叙事后,终于要向“数据竞争”和“防御性编程”这两个顽疾开刀了?

今天,我们就来看看复盘这份长达 8 年的提案,剖析一下“不可变性”对 Go 意味着什么,以及它面临的巨大挑战。

痛点:防御性拷贝的代价

在 Go 1.x 的世界里,我们为了保证数据的安全性,往往需要付出高昂的代价。

假设你有一个包含敏感配置的结构体,你想把它暴露给其他包,但又不希望它被修改:

type Config struct {
    Servers []string
    // ...
}

// 现在的做法:为了安全,必须返回拷贝
func (c *Config) GetServers() []string {
    out := make([]string, len(c.Servers))
    copy(out, c.Servers)
    return out
}

这种“防御性拷贝”带来了两个严重问题:

  1. 性能损耗:每次访问都要分配内存和复制数据,对于热点路径是不可接受的。
  2. 语义模糊:如果我不拷贝,直接返回 c.Servers,调用者能不能改?文档说不能改,但这只是君子协定,编译器不会阻止手滑的程序员。

正如提案作者 romshark 所言:“我们现在的做法,要么是不安全的(直接返回指针),要么是低效的(防御性拷贝)。”

而不可变类型(Immutable Types)的引入,旨在提供第三种选择:既安全,又高效。

提案核心:immut 限定符

NO.27975 提案的核心思想非常直接:引入一个新的类型限定符(最初建议重载 const,后倾向于引入immut ),让编译器来强制执行“只读”契约。

想象一下这样的 Go 代码:

// 定义一个只读的切片类型
func ProcessData(data immut []byte) {
    // 读取是 OK 的
    fmt.Println(data[0]) 

    // 修改是编译错误的!
    // data[0] = 'X' // Compile Error: cannot assign to immutable type
}

在这个愿景中,不可变性是类型系统的一部分。

  • 赋值限制:你不能把一个 immut 类型的变量赋值给一个 mut(可变)类型的变量,这防止了“权限逃逸”。
  • 传递性:如果一个结构体是不可变的,那么它字段指向的所有数据(如切片、映射、指针)也自动变为不可变。

这看起来很像 Rust 的 & (immutable reference) 和 &mut (mutable reference),或者 C++ 的 const。但 Go 社区的讨论,揭示了这背后远比想象中复杂的工程难题。

社区激辩:理想与现实的碰撞

这份提案下的讨论区,堪称 Go 语言设计哲学的“修罗场”。Ian Lance Taylor, Rob Pike 等核心大佬纷纷下场,与社区开发者展开了长达数年的拉锯战。

const 污染

这是 Ian Lance Taylor 最担心的问题。如果你把一个底层函数的参数标记为 immut,那么所有调用它的上层函数,为了传递这个参数,往往也需要把自己的变量标记为 immut。

这种“传染性”会导致代码库中充斥着 immut 关键字。更糟糕的是,如果你以后需要修改底层函数,让它对数据进行一点点修改,你需要修改整个调用链上的类型签名。这在 C++ 中被称为“const correctness”的噩梦。

io.Writer 的尴尬

bcmills 提出了一个极其尖锐的兼容性问题:现有的 io.Writer 接口定义是 Write(p []byte)。

  • 如果我们把 p 改成 immut []byte,那么现有的所有 Write 方法实现都会破坏兼容性。
  • 如果我们不改,那么即使我手里有一个只读的切片,我也没法把它传给 io.Writer,因为类型不匹配。

这似乎陷入了一个死循环:要么破坏所有现有代码,要么新特性无法与标准库兼容。

所谓“不可变”,到底是谁不可变?

jimmyfrasche 指出了一个微妙的语义陷阱。

在 C++ 中,const T& 只是意味着“我不可以通过这个引用去修改它”(Read-only View),并不意味着“这个数据本身不会变”。因为可能还有另一个非 const 的指针指向同一块内存,并且正在修改它。

如果是前者(只读视图),它无法解决并发安全问题(数据竞争依然存在)。如果是后者(真正的内容不可变),那么 Go 必须引入一套类似 Rust 的所有权(Ownership)系统来保证“没有其他人在写”。这对于 Go 来说,改动太大了。

为何现在重提?

既然困难重重,为何在 2026 年的今天,这个提案又被翻出来了?

我认为有几个关键因素:

首先,泛型的“降维打击”。以权限泛型(Permission Genericity)化解兼容性死结。

前面提到了,在 Go 1.18 泛型落地之前,不可变性提案面临着一个被称为“io.Writer 陷阱”的致命矛盾:如果将 io.Writer.Write(p []byte) 改为接受 immut []byte,将导致全世界现有的实现代码因签名不匹配而崩溃;如果不改,只读数据又无法直接传入。

泛型的引入为这一难题提供了全新的解题思路。通过类型约束中的联合类型(Union Types),我们可以实现所谓的“权限泛型性”。这意味着 mutability(可变性)不再是一个硬编码的死结,而可以作为一个类型参数(Type Parameter)来处理。

想象一下,我们可以利用泛型约束定义一个覆盖“可变”与“不可变”两种状态的超集:~[]byte | ~immut []byte。下面是在这种模式下的一个泛型化的Writer接口:

// 这是一个设想中的“权限泛型”接口
type Writer[T ~[]byte | ~immut []byte] interface {
    Write(p T) (n int, err error)
}

泛型化的 Write[T ~[]byte | ~immut []byte](p T) 方法,在逻辑上可以产生如下影响:

  1. 权限无关的调用:由于约束涵盖了两种类型,调用者现在可以合法且安全地将 immut []byte 传给标准库函数,解决了“只读数据无法写入”的窘境。
  2. 非破坏性的兼容:对于现有的实现者(如 bytes.Buffer),其原本定义的 Write([]byte) 签名可以被视为该泛型约束的一个特化实例。编译器可以在不改动任何旧代码、不引入任何运行时开销的前提下,在静态分析阶段完成权限的自动适配与校验。

其次,性能压力的倒逼。

随着 Go 在高性能领域的应用越来越深(如数据库、AI 推理),对于“零拷贝”的需求越来越强烈。能够安全地共享内存,是提升性能的关键。

最后是安全性需求。

在并发编程中,数据竞争依然是 Go 程序的头号杀手。go vet 和 race detector 虽然好用,但它们是运行时的、滞后的。社区渴望一种编译期的保证。

未来的可能性:温和的演进

虽然完全的“不可变类型”可能依然很难落地,但我们可以期待一些更温和的替代方案:

  • 只读视图 (Read-only Views):不是引入新的关键字,而是引入一种新的泛型类型 ReadOnly[T],或者编译器内置的 view 类型。
  • 纯函数检查:引入一种机制,标记某些函数是“无副作用”的,从而允许编译器进行更激进的优化。
  • 静态分析增强:不改变语言规范,而是通过更强大的 vet 工具,利用注释或特定命名约定,来静态检查不可变性约束。

小结

NO.27975 提案的“复活”,是一个信号。它表明 Go 团队并没有满足于现状,依然在探索如何在保持“简单”这一核心价值观的同时,赋予语言更强的表达力和安全性。

无论最终结果如何,这都是 Go 语言演进史上值得铭记的一笔。它提醒我们:在软件工程中,没有免费的午餐,每一个简单的特性背后,都是无数次复杂的权衡。


你支持引入 immut 吗?

面对“性能”与“简单”的博弈,你是否愿意为了消除数据竞争而接受 immut 带来的“类型传染”?在你的项目中,是否也曾深受“防御性”的性能困扰?

欢迎在评论区分享你的看法,或者聊聊你最期待的 Go 演进方向!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats