<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Golang on Zampo Blog</title><link>https://blog.cpdd.fyi/categories/golang/</link><description>Recent content in Golang on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.cpdd.fyi/categories/golang/index.xml" rel="self" type="application/rss+xml"/><item><title>你的旧 Go 代码，Go 1.26 能一键救回来</title><link>https://blog.cpdd.fyi/posts/go-125-126-automatic-modernization/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate><guid>https://blog.cpdd.fyi/posts/go-125-126-automatic-modernization/</guid><description>&lt;p&gt;你有没有这种代码——&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Go 1.22 之后可以写成 &lt;code&gt;for range n&lt;/code&gt;。你知道，但三年了，你还没改。&lt;/p&gt;
&lt;p&gt;或者这种——&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexByte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Go 1.18 就有 &lt;code&gt;strings.Cut&lt;/code&gt; 了。你知道怎么用，但一直没动手。&lt;/p&gt;
&lt;p&gt;我猜你的项目里不止一处这种「知道有更好的写法，但优先级排不上号」的代码。你甚至列过 Refactoring 工单，然后排到了下周，再然后就没有然后了。&lt;/p&gt;
&lt;p&gt;这不是你的问题。这是大多数 Go 项目的真实状态：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;知道该改，但没人专门花时间去改。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但从 Go 1.25 开始，情况变了。不是因为你变勤快了，而是工具链开始替你动手了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="真正的主角不是新特性是-go-fix"&gt;真正的主角不是新特性，是 &lt;code&gt;go fix&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;每到一个新版本，我们习惯先把 release notes 扫一遍，看有没有好用的新语法、新标准库。&lt;/p&gt;
&lt;p&gt;但 Go 1.25 和 1.26 真正重要的变化，不在新特性本身，而在一个你可能忽略的子命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ go fix ./...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;go fix&lt;/code&gt; 不是什么新东西。它从 Go 1 就有了，以前只是偶尔用来修一下 build tag 格式之类的小补丁。但在 Go 1.26 里，它被彻底重写了——底层不再是独立代码，而是和 &lt;code&gt;go vet&lt;/code&gt; 共用了同一个分析框架。&lt;/p&gt;</description></item><item><title>一个 goroutine 崩了，为什么你的 recover 根本接不住？</title><link>https://blog.cpdd.fyi/posts/go-defer-panic-recover/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate><guid>https://blog.cpdd.fyi/posts/go-defer-panic-recover/</guid><description>&lt;p&gt;你在 HTTP 服务最外层加了 recovery middleware，以为兜底已经做完。后来某个 handler 里随手起了一个 goroutine；它空指针了，整个进程还是退出。&lt;/p&gt;
&lt;p&gt;这不是 middleware 没挂上，也不是 recover 偶发失灵。是那个 goroutine 从来不在你的 recover 覆盖范围里。&lt;/p&gt;
&lt;p&gt;很多人把 &lt;code&gt;defer&lt;/code&gt; 记成“后进先出”，这句话没错，却少了决定线上行为的半截：&lt;strong&gt;defer、panic 和 recover 都是在当前 goroutine 上完成的。&lt;/strong&gt; 你放进一个函数的清理动作，没法替另一个 goroutine 收尾；你放在外层的 recover，也接不住另一个 goroutine 的 panic。&lt;/p&gt;
&lt;p&gt;这篇不复述语法。只解决几个在代码评审和事故现场会真正出问题的判断：&lt;code&gt;defer&lt;/code&gt; 到底放在哪里，循环里为什么危险，recover 为什么看起来写了却没用，以及 panic 到底该不该出现在业务代码里。&lt;/p&gt;
&lt;h2 id="defer-不是一摞万能清理卡它属于当前-goroutine"&gt;&lt;code&gt;defer&lt;/code&gt; 不是一摞万能清理卡，它属于当前 goroutine&lt;/h2&gt;
&lt;p&gt;从语言语义看，&lt;code&gt;defer&lt;/code&gt; 把一次函数调用登记下来，外层函数返回时再按后进先出的顺序执行。参数在登记那一刻就已经求值；它也能读写命名返回值。&lt;/p&gt;
&lt;p&gt;这套规则足够写业务，但解释不了两个现象：为什么循环里的 &lt;code&gt;defer rows.Close()&lt;/code&gt; 会拖住资源，为什么父函数的 recover 接不住子 goroutine。&lt;/p&gt;
&lt;p&gt;把视角往 runtime 推一步，老路径里每次执行 defer，会形成一个 &lt;code&gt;_defer&lt;/code&gt; 记录。Go 1.25 的 runtime 源码里，&lt;code&gt;_defer&lt;/code&gt; 有 &lt;code&gt;sp&lt;/code&gt;、&lt;code&gt;pc&lt;/code&gt;、&lt;code&gt;fn&lt;/code&gt; 和 &lt;code&gt;link&lt;/code&gt; 字段；&lt;code&gt;link&lt;/code&gt; 的注释写得很直接：它是 &lt;strong&gt;next defer on G&lt;/strong&gt;。这里的 G 就是 goroutine。&lt;/p&gt;</description></item></channel></rss>