<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Golang on LCY's Notes</title><link>https://blog.lcy.one/tags/golang/</link><description>Recent content in Golang on LCY's Notes</description><generator>Hugo</generator><language>zh-tw</language><copyright>lcy</copyright><lastBuildDate>Tue, 16 Apr 2024 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.lcy.one/tags/golang/feed.xml" rel="self" type="application/rss+xml"/><item><title>Golang 新手的抱怨</title><link>https://blog.lcy.one/2024/04/16/golang-newbie-complaints.html</link><pubDate>Tue, 16 Apr 2024 00:00:00 +0800</pubDate><guid>https://blog.lcy.one/2024/04/16/golang-newbie-complaints.html</guid><description>&lt;p&gt;睽違了兩年多的部落格更新&lt;br&gt;
是要來抱怨這幾個月學 + 用 Golang 的使用體驗&lt;/p&gt;</description><content:encoded><![CDATA[<p>睽違了兩年多的部落格更新<br>
是要來抱怨這幾個月學 + 用 Golang 的使用體驗</p>
<h2 id="個人使用程式語言的經驗">個人使用程式語言的經驗</h2>
<p>我個人算上學生時代除了作業以外，開始會自己課餘寫程式開始<br>
大概有八年的程式開發經驗</p>
<p>其中最熟 Python，不管業餘還是工作上都有用到，寫了六年多<br>
不過沒有碰過 ML、AI、資料分析之類的<br>
主要是在寫後端服務和一些有大有小的工具程式</p>
<p>Java 是在工作上寫過，接觸約兩年<br>
JavaScript 工作上和業餘斷斷續續寫過<br>
工作上也有多少碰到 C、C++、Groovy、Lua 之類的，大概就到略熟悉的程度<br>
還有碰過像是架這個網站用的 Ruby，之前寫過的 PHP，不過都只有入門等級而已</p>
<h2 id="開始寫-golang">開始寫 Golang</h2>
<p>最近在公司裡被轉去一個新領域，主要也是開發後端服務<br>
大部分的團隊都是用 Golang 開發，多數專案也是 Golang 寫的<br>
雖然真的硬要用 Python 開發可能也可以<br>
不過一方面懶得去爭論，另一方面想說學一下這個也算熱門的語言也不錯</p>
<h2 id="寫這篇文章的原因">寫這篇文章的原因</h2>
<p>邊學邊寫 Golang 大概三個月了<br>
目前都覺得越學越覺得不太喜歡這語言，覺得不吐不快</p>
<p>以下列出一些我覺得的優缺點，主要還是跟我最熟悉的 Python 比較</p>
<h2 id="覺得-golang-的優點">覺得 Golang 的優點</h2>
<p>先提一些優點</p>
<ul>
<li>
<p>效能<br>
雖然可能略輸 C/C++<br>
不過在大部分的情況下肯定比 Python 和 JavaScript 快</p>
</li>
<li>
<p>單一二進制檔案<br>
不用像 Python 和 JavaScript 之類的語言，部署的時候要想辦法準備環境<br>
Golang 是編譯成二進制檔案，直接執行就好</p>
</li>
<li>
<p><code>go fmt</code><br>
官方直接有格式化工具，不用像其它大部分的語言要另外裝第三方工具<br>
開發者沒有藉口不跑格式化</p>
</li>
</ul>
<h2 id="覺得-golang-的缺點">覺得 Golang 的缺點</h2>
<p>接下來就是各種抱怨了</p>
<h3 id="缺乏文件">缺乏文件</h3>
<p>不確定具體原因，但我覺得各種 Golang 套件的文件都不是很完整<br>
常常都要進去看原始碼才知道怎麼用</p>
<h4 id="例子gin">例子：Gin</h4>
<p>像是 Golang 前幾大的 web 框架－ <a href="https://gin-gonic.com/docs/">Gin 的文件</a><br>
根本沒有多少教學或說明，只有一些帶有一兩句說明的範例<br>
API reference 很多也都只有簡短說明，有時候都看不出具體的用法<br>
看起來完全不像是一個有 75k+ stars 的專案<br>
這個也有人開 <a href="https://github.com/gin-gonic/gin/issues/3152">issue</a> 在抱怨</p>
<h4 id="例子gomock">例子：gomock</h4>
<p><a href="https://github.com/golang/mock">gomock</a> 是用來產生 mock 物件的工具<br>
Google 後來不維護了，Uber <a href="https://github.com/uber-go/mock">接去維護</a><br>
同樣也是只有基礎的教學<br>
像是比較進階的 <code>matcher</code> 用法要自己點進去看原始碼參考</p>
<h4 id="對比-python-套件的文件">對比 Python 套件的文件</h4>
<p>以熱門的 web 框架來說<br>
不論是 <a href="https://flask.palletsprojects.com">Flask</a>、<a href="https://docs.djangoproject.com">Django</a>，還是 <a href="https://fastapi.tiangolo.com/">FastAPI</a> 的文件都遠遠比 Golang 的 Gin 完整<br>
不用一直去翻原始碼，看看文件就知道怎麼用</p>
<h3 id="錯誤處理">錯誤處理</h3>
<p>Golang 沒有 try-catch<br>
通常都是讓函式多回傳一個 <code>error</code> 物件<br>
再從呼叫方 <code>if err != nil</code> 來判斷是否有錯誤發生</p>
<p>我個人是覺得這種作法寫起來沒有 try-catch 直覺<br>
而且相較於 Golang 連要分辨錯誤的類型都有點麻煩<br>
Python 那樣可以做 <a href="https://docs.python.org/3/tutorial/errors.html#exception-chaining">exception chaining</a>、<a href="https://docs.python.org/3/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions">exception grouping</a><br>
還可以根據 exception 的 parent class 來捕捉不同類型的錯誤<br>
整個體驗差滿多的</p>
<h3 id="套件管理">套件管理</h3>
<p>Golang 在 1.11 之後加入 module 的概念<br>
套件管理用起來就會比較像 Python 的 Pipenv 或是 JavaScript 的 npm<br>
把套件的版本號寫在 <code>go.mod</code> 裡面<br>
工具自動產生套件的 hash 值和版本號到 <code>go.sum</code></p>
<p>不過相較於 <a href="https://pipenv.pypa.io/en/stable/pipfile.html#package-category-groups">Pipenv</a> 和 <a href="https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file">npm</a> 有 dev dependencies 的概念<br>
Golang 這邊沒有將 dependencies 分類<br>
雖然 Golang 不像 Python 和 JavaScript 需要在執行環境安裝套件<br>
但是仍然可能會有一些「tool dependencies」是只用在開發或 CI/CD 的時候</p>
<p><a href="https://go.dev/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module">官方的解法</a>是把這些套件寫進 <code>tools.go</code><br>
這樣 build 的時候就不會把這些套件包進去<br>
&hellip;這什麼繞路的解法，加一個 tool dependency 還要記得改兩個地方</p>
<h3 id="缺少一些語言特性">缺少一些語言特性</h3>
<p>這個有可能只是因為我還不習慣寫 Golang<br>
Golang 作為一個相對新的熱門程式語言<br>
（2009年發行，Python 3.0 2008年發行，其它熱門語言就更舊了）<br>
很多寫起來還是很有「老」程式語言的感覺</p>
<p>直接到官方的 GitHub issues 頁面<a href="https://github.com/golang/go/issues?q=is%3Aissue&#43;is%3Aopen&#43;sort%3Areactions-%2B1-desc">以按讚排序</a><br>
以現在的時間點（2024 年 04 月 16 日）來看就有幾個我遇過的障礙</p>
<ul>
<li>
<p>第二名：<a href="https://github.com/golang/go/issues/19814">proposal: spec: add typed enum support</a><br>
沒有 enum 可以用，只能用 <code>const</code> 來模擬</p>
<p>只寫 Golang 就算了，開發後端服務免不了要跟前端或其它服務溝通<br>
不論是 HTTP API 常用的 OpenAPI<br>
或是 gRPC API 常用的 Protocol Buffers 都有 enum 這個類別<br>
在 Golang 要跟這些資料格式轉換的時候就會有點麻煩</p>
</li>
<li>
<p>第五名：<a href="https://github.com/golang/go/issues/45624">proposal: expression to create pointer to simple types</a><br>
沒有辦法用簡單的語法直接建立一個指向新變數的指標</p>
<p>會常撞到這個問題的其中一個情境跟 Golang 的一個語言特性有關<br>
就是 struct 建立起來沒有賦值的話就會是「<a href="https://golang.org/ref/spec#The_zero_value">zero value</a>」<br>
int 會是 0、string 會是空字串、bool 會是 false，而不會是 nil 之類的<br>
也就是說沒有辦法分辨真的是 0/空字串/false 還是沒有賦值<br>
常用解法是把這些欄位的類別改成指標，然後判斷是否是 nil 來分辨是否有賦值</p>
<p>不過要塞欄位時就麻煩了<br>
就算只是要塞一個固定的值，還是要先建立一個變數，才能再把變數的指標塞進去</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">MyStruct</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldA</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldB</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">foo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">20</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">bar</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">myStruct</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">MyStruct</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldA</span><span class="p">:</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">foo</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldB</span><span class="p">:</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">bar</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>不然就是要另寫一個函式來取出指標</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">MyStruct</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldA</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldB</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">IntPointer</span><span class="p">(</span><span class="nx">i</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">i</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">myStruct</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">MyStruct</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldA</span><span class="p">:</span><span class="w"> </span><span class="nf">IntPointer</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fieldB</span><span class="p">:</span><span class="w"> </span><span class="nf">IntPointer</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>像 AWS 的 Golang SDK 就寫了<a href="https://github.com/aws/aws-sdk-go/blob/49079a/aws/convert_types.go#L5">好幾個函式</a>在做這件事</p>
</li>
</ul>
<h2 id="結語">結語</h2>
<p>其實還有不少小問題<br>
不過就先列這些比較大的障礙<br>
不知道再多接觸一段時間會是開始接受<br>
還是會有更多抱怨可以寫&hellip;</p>]]></content:encoded></item></channel></rss>