『八股文™』詳解 Go 中的深拷貝與淺拷貝

大家好,我是 moooofly。

今天我們來聊一聊:Go 語言中關於深拷貝和淺拷貝的問題。

0x01 基礎概念

在 Go 語言中,針對如下結構體,如何對其進行淺拷貝深拷貝

代碼示例如下

上述問題的核心在於

上述問題很常見,於是我們可以繼續追問如下:

  1. 淺拷貝後的數據關係

在示例代碼中可以看出,“淺拷貝” 對應的就是一條簡單的賦值語句。

淺拷貝的本質是逐字段值複製

正是因爲存在 “底層數據被共享” 的問題,才產生了 “深拷貝” 的概念,因爲在很多場景中,共享底層數據會導致意想不到的事情發生。

  1. 深拷貝相關數據類型

除了切片,以下數據結構同樣需要顯式地進行深拷貝處理:

a. map

針對 map 的淺拷貝僅會複製其指針,導致底層哈希表數據會被共享,需要新建 map 並遍歷鍵值對重新插入完成深拷貝

b. 指針

針對指針的淺拷貝僅會複製對應的地址,深拷貝時複製的是其指向的實際內容

c. Channel

針對 Channel 的淺拷貝會導致共享同一通道實例,因此針對 Channel 的深拷貝其實是以新建的方式完成的

d. 函數(Func)

函數本身是引用類型,淺拷貝後會共享同一函數實例,需要通過重新定義函數實現的方式達成深拷貝效果

e. 不可淺拷貝的 sync.Mutex 和 sync.RWMutex

**sync.Mutex **鎖狀態是與協程綁定的,淺拷貝後會導致不可預測的併發行爲,需在深拷貝時需重置鎖狀態或標記爲不可複製。

  1. 小結

以下是 Go 語言中數據類型在深拷貝 / 淺拷貝場景下的安全性總結表,涵蓋常見類型的複製行爲和底層原理:

最佳實踐:

0x02 如何實現一個通用的深拷貝函數

假設需要實現一個通用深拷貝工具函數,支持任意結構體,應該如何做?

常見的方案有如下兩種:

  1. 利用 encoding/gob 或 encoding/json 序列化反序列化實現深拷貝

  2. 基於 reflect 實現深拷貝

  3. 基於 json 實現深拷貝


基於 json 或 gob 序列化實現通用深拷貝簡單直接,但需要針對循環引用和非導出字段進行特殊處理

  1. 基於 reflect 實現深拷貝

基於白名單機制,僅處理可安全複製的類型

反射 + 遞歸通過 reflect 包動態處理嵌套結構,但需注意性能開銷

上述兩種方案都存在一些侷限,在實際使用過程中,還需要做一些額外的設計調整。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/o_no8VBJeclwobMfKI6M3A