第三篇 Go 問答總結,2019 年 8 月份總結,大約有 12 篇問答。前兩遍地址如下:
問題大部分是來自於知乎和 segmentfault。本月有一個問題來自 stackoverflow,我的英文水平一般,讀與翻譯還行,但寫起來還需要鍛鍊。雖然這一個回答沒得到一個贊同,但能被題主採納,我還是很榮幸的。
最近發現,我的回答經常會被 Go 語言中文網的週刊收錄。對 Go 感興趣的朋友可以關注下 Go 語言中文網的公衆號,內容還是非常豐富的,每天都會推送關於 Go 的優秀文章。
開始正文!
dynamodbattribute.UnmarshalMap canges the type of my variable to map[string]interface{}
將 stackoverflow 的這篇回答放在首位吧,問題不是很難,重在第一次嘗試,stackoverflow 上面有價值的問題還是很多的。
題主的目標是希望 map 類型轉化成 struct 類型。將問題稍微簡化下,題主希望通過類似如下這種寫法將 map[string]interface{} 轉化爲 user 類型。
package main
import "fmt"
type User struct {
Name string
}
func Item() interface{} {
return User{}
}
func ItemMap(s map[string]interface{}, item *interface{}) {
*item = s
}
func main() {
m := Item()
fmt.Printf("%T\n", m)
fmt.Printf("%v\n", m)
s := map[string]interface{}{
"Name": "poloxue",
}
ItemMap(s, &m)
fmt.Printf("%T\n", m)
fmt.Printf("%v\n", m)
}
首先,通過 Item() 方法創建一個 User 類型變量,但返回的類型是 interface{},因而 main 主函數中的 m 通過類型推導,也就是 interface{} 類型,在 ItemMap 函數中將 map[string]interface{} 變量賦值給 m 的地址並不會有任何問題,因爲雖然此時 m 類型的底層類型是 User,但賦值時並不會驗查到這一層,經過一部分之後,m 的底層類型就由 User 轉化成了 map[string]interface,並非完成了 map[string]interface{} 到 struct 的轉化。
看樣子,map 和 struct 的轉化還是很常見的需求啊,上月的問答總結就有類似的問題。再次推薦 mapstructure 包。
Go 是內置併發支持的語言,併發的常見問題就是出現競態條件,Go 專門提供了一個工具用於檢測這個問題,使用非常方便,只需要在執行程序時加上 -race 選項,Go 源碼中的這個 raceenable 表示的意思即使是否啓用了 raceenable。
前段時間翻譯了一篇關於 go race 的文章,訪問 Go 譯文之競態檢測器 race。
Golang中,runtime.Caller(skip),爲什麼會保留編譯器變量?
題主的目標是獲取 Go 可執行文件的當前路徑,但對於很多習慣了使用解釋型語言的朋友,養成了源碼文件路徑即可執行文件路徑的思維,而 runtine.Caller 可用於查看調用棧信息,正好可以保留編譯時的源碼文件信息。
但真實的項目部署的肯定是可執行文件,而非源碼文件,而路徑也是針對可執行文件的路徑。如何獲取到呢?請看回答吧!
再提醒一點,正因爲很多朋友養成了解釋型語言的思維,常會通過 go run 執行源碼,然後獲取可執行文件路徑,但最終得到的確實一個看不懂的路徑。這個問題在回答也有介紹。
golang 中的標準庫 database/sql 已經提供了一份數據庫管理的公共實現,同樣涉及 mysql 連接複用的功能。
題主的問題是爲什麼沒有達到複用的效果。
連接複用有一個重要前提就是資源使用結束後要記得釋放,否則將會一直處於佔用狀態,至於有哪些情況會導致連接一直複用,查看回答!
回答中提到了一篇非常系統介紹 Go 中 database/sql 使用的教程,有興趣可以仔細讀讀。
這個問題稍微有點複雜。異常的原因,簡單說來是 channel 發送者發送等待時間太長,導致異常出現。解析問題的核心思路在於,在發送者和接收之間增加一個 buffer,無論發送者發送了什麼內容,先將消息接收到 buffer 中,待有空閒的接收者時將消息從 buffer 中發送出去。
題主是通過 chan 自帶的 buffer 實現,創建了緩衝大小 50 的 chan。當然,更靈活的方式,還是自己實現 buffer 比較好。而且題主的場景是爬蟲,可能對 buffer 的大小要求會比較高,可能適當考慮接入外部服務。
go的reflect是否可以獲取到同一個package中,其他文件定義的類型的私有方法的名稱?
首先,要明確的是,Go 中的反射是無法看到類型的私有方法的。如果想達到這個目標,只能將方法定義爲可導出方法。但私有方法也有它的好處,那就是外界無法使用這個方法。那將方法定義爲公開可導出是否也可以做到讓外界無法使用?
查看我的回答吧!
beego 中緩存的存儲與獲取的問題,很簡單,沒什麼好說的!
問題的核心不在於 select 的工作機制,而是關於 Go 中 timer 定時器的使用問題。以下是問題的核心代碼。
for {
select {
case num := <-ch:
fmt.Println("get num is ", num)
case <-time.After(2 * time.Second):
fmt.Println("time's up!!!")
//quit <- true
}
}
}()
題主的問題是爲什麼 timer 的 case 一直沒有執行。這是因爲 time.After 在每次循環都會創建的 chan,之前定時 chan 已經不再監聽了。
我開始覺得,這種方式挺適合處理任務出現 timeout 的情況,經過一位朋友提醒,這裏面存在着泄露風險。具體原因,閱讀回答瞭解吧!
在golang的設計裏,爲什麼不能用switch實現select的功能?
主要是場景決定吧,channel 監聽有點類似於異步 IO 的多路複用,select 可以說是多路複用的專用名字了,switch 是分支結構,硬要加上其他功能,也不便於理解,我的感覺是,獨立出一個新的關鍵字會更好理解。
題主要求用從淺入深的方式介紹,Go 中 struct 含有函數成員的問題。我覺得我也寫得還不多,但是沒有得到題主的採納,只得到一個評論誇讚。
肯定是分的,但我們不用關心,go 命令已經封裝好了,它會依據平臺選擇不同的底層命令。
在 go 安裝目錄的 pkg 目錄下有個 tools 目錄,裏面包含了編譯鏈接時實際使用的命令,比如我的 Mac Pro,在 pkg/tool/darwin_amd64/ 下能找到 go 編譯鏈接實際調用的命令 compile 和 link。darwin_amd64 中 drawin 表示操作系統,amd64 就是系統架構。
執行 go env 會發現其中有個環境變量 GOTOOLDIR,它是那些與我們平臺相關工具的所在目錄。我的 Mac 下得到的結果是:
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
題主的問題是如何進行 html 的解析。
<ul id="pages">
<li><a href="xxx1">1</a></li>
<li><a href="xxx2">2</a></li>
<li><a href="xxx3">3</a></li>
</ul>
這類問題一般是不推薦使用正則匹配,可以試試 goquery 這個庫,它的用法類似 jquery,可用於解析 html 內容,使用非常方便。當然,回答中也介紹了正則的實現,不過代碼的可讀性看起來比較差。
本月的回答總結完畢,如果大家發現有不當的內容,歡迎批評指正,非常感謝。
歡迎關注我的個人微信公衆號。