等不及 go 泛型釋出,我先實現了(類似 C++ 的 template)

shanjin發表於2020-06-17

https://github.com/PioneerIncubator/betterGo 這個專案就是我帶著一位師弟實現的,有興趣的同學一起開發哈~

betterGo

Better Go implement parts that I think Golang missed

Real Generic

Provide the real interface{} to you so that you can use it in your code. Before deployment, just use translator to generate specify type code, in which way will not affect your performance.

Here are all generic functions:

  • enum.Reduce
  • enum.Map

Implementation

Use go ast to analyse your code where using generic functions, generate specify function for your types and replace your original call.

What I actually do

I do this shit for you :P

betterGo

Join Us

wechat group

Background

現在的 Go 語言不支援泛型(像 C++ 中的 template、Java 中的 interface)

目前,為實現泛型的需求,在 Go 語言中往往有如下幾種方式1

  1. Interface (with method) 優點:無需三方庫,程式碼乾淨而且通用。 缺點:需要一些額外的程式碼量,以及也許沒那麼誇張的執行時開銷。
  2. Use type assertions 優點:無需三方庫,程式碼乾淨。 缺點:需要執行型別斷言,介面轉換的執行時開銷,沒有編譯時型別檢查。
  3. Reflection 優點:乾淨 缺點:相當大的執行時開銷,沒有編譯時型別檢查。
  4. Code generation 優點:非常乾淨的程式碼 (取決工具),編譯時型別檢查(有些工具甚至允許編寫針對通用程式碼模板的測試),沒有執行時開銷。 缺點:構建需要第三方工具,如果一個模板為不同的目標型別多次例項化,編譯後二進位制檔案較大。

betterGo就是通過code generation來實現泛型

如何使用

想用一下 betterGo 的,可以看看這個例子哈:

克隆程式碼後,我們做了測試例子,程式碼就是test/map/map.go,你正常用interface{} 的函式就是Enum.Map 這樣子用。

然後想生成具體型別的函式,就執行這行命令:go run main.go -w -f test/map/map.go

然後你發現 test/map/map.go 改變了,Enum.Map 變成了: enum.MapOriginFn(origin, fn)

然後你看專案底下生成了: utils/enum/map.go,就是具體型別的函式

參與專案

關於參與專案的話,可以直接看程式碼,然後看到 ast 相關的包,就簡單進去看看,猜猜什麼意思,應該就可以理解這個專案以及程式碼了。

如果想從理論出發的話,可以簡單看看這本書:https://github.com/chai2010/go-ast-book ,其實他也就是把 ast 包裡的程式碼簡單講講。

想參與具體開發的話,又沒有想改進的地方,可以看看 issue 列表哈:https://github.com/PioneerIncubator/betterGo/issues

技術思路

  1. 匯入需要操作的檔案,可以是檔案/目錄

  2. 通過 AST 進行語法分析

AST 能分析出每條語句的性質,如:

  • GenDecl (一般宣告):包括 import、常量宣告、變數宣告、型別宣告
  • AssignStmt(賦值語句):包括賦值語句和短的變數宣告 (a := 1)
  • FuncDecl(函式宣告)
  • TypeAssertExpr(型別斷言)
  • CallExpr(函式呼叫語句)
  1. 當分析到包含變數的值/型別的語句時 (AssignStmtFuncDecl) 會對變數的值和型別進行記錄,並建立二者之間的對映關係,以便於在後續環節中能夠通過變數名獲取變數的型別

  2. 當發現函式呼叫語句 (CallExpr) 時,會檢查該函式是否為我們提供的函式,如果是,則通過上一步中記錄的引數名對應的型別生成專門處理該型別的一份程式碼,並儲存到指定路徑下(如果之前已經生成過相同型別的程式碼則不重複生成)

  3. 將原始碼中的原來的函式呼叫語句替換成新的函式呼叫語句,使其呼叫上一步中新生成的函式,並更新 import 的包

Reference

- [1] Go 有什麼泛型的實現方法? - 達的回答 - 知乎
更多原創文章乾貨分享,請關注公眾號
  • 等不及 go 泛型釋出,我先實現了(類似 C++ 的 template)
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章