<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      Go 1.26 內置函數 new 新特性

      目前golang 1.26的各種新特性還在開發中,不過其中一個在開發完成之前就已經被官方拿到臺面上進行宣傳了——內置函數new功能擴展。

      每個新特性其實都有它的背景故事,沒有需求的驅動也就不會有新特性的誕生。所以在介紹這個新特性之前我們先來了解下是什么樣的場景催生了這個功能。

      如果你經常瀏覽一些大型的go項目,尤其是那些需要頻繁和JSON、GRPC或者yaml打交道的項目,比如k8s,你會發現這些代碼庫會提供一些和下面代碼類似的幫助函數:

      func getPointerValue[T any](v T) *T {
      	return &v
      }
      

      這個是我用泛型改寫的,代碼庫里通常都是getIntPointerValue(int) *int這樣非泛型函數。函數的作用很簡單,返回指向自己參數的指針。但這樣簡單的三行代碼有什么用呢?

      用處有好幾個,第一個是在json或者rpc里有時候我們會用指針的nil來表示這個值沒有生效,和字段類型的零值做區分,但這使得給字段賦值變麻煩了:

      type Data struct {
          Num *uint
      }
      
      d := &Data{}
      d.Num = &12345 // 編譯錯誤
      d.Num = getPointerValue(12345)
      

      這行代碼d.Num = &12345是語法錯誤,因為在golang里規定不能對字面量以及常量取地址。不僅如此,類似d.Num = &getNum()這樣的代碼也是無法編譯的,因為go也規定了不能對右值取地址。

      如果沒有幫助函數,我們需要用一個中間變量接住這些值,然后再把這個中間變量的指針賦值給結構體的字段。

      第二個作用在于防止潛在的內存泄漏:

      type BigStruct struct {
          // 100個其他字段
          Num int
      }
      
      bigObj := &BigStruct{....}
      bigSlice := make([]int, 1024)
      
      d1.Num = &bigObj.Num
      d2.Num = &bigSlice[1000]
      

      猜猜如果d1d2需要很長時間才能被釋放會發生什么。答案是bigObjbigSlice也會一直存在不被釋放,因為golang中結構體、數組/切片只要還有指針指向自己的字段或者元素,那么整個結構體和數組/切片的內存都不能被釋放。換句話說因為你的Data結構體持有了一個8字節的指針,會導致它背后十幾KB的內存一直沒法釋放,盡管這些內存中的99%你完全用不到。這在比較寬泛的定義上已經屬于是內存泄漏了。

      所以這時候幫助函數就起作用了。getPointerValue的參數不是指針,因此會把傳進來的值拷貝一份,然后再取拷貝出來的新變量的指針,這樣就不會有指針指向那些大對象的字段或者元素了,這些大對象也可以盡快得到釋放從而不會浪費內存。

      背景故事到此結束,到這里其實你也能猜出new被擴展的新功能大致是什么了。

      new在1.26中獲得的新功能是可以接受一個表達式,它會復制表達式的結果到同類型的變量里并返回指向這個變量的指針。

      看個例子:

      new(1234) // *int, 指向的值是1234
      
      func getString() string {
          return "apocelipes"
      }
      new(getString()) // *string, 指向的值是"apocelipes"
      
      s := "Hello, "
      new(s + getString() + "!") // *string, 指向的值是表達式的結果"Hello, apocelipes!"
      

      功能很簡單,相當于把上面的幫助函數getPointerValue集成到了現有的內置函數new里。這能讓我們簡化一些代碼。

      不過按照go團隊以往的做法,如果只是簡化代碼的話其實是不會在原有的內置函數上新增功能的。現在這么做了說明還有額外的好處——性能。

      我們看個性能測試:

      func BenchmarkOld(b *testing.B) {
      	for b.Loop() {
      		p := getPointerValue(123)
      		if p == nil || *p != 123 {
      			b.Fatal()
      		}
      	}
      }
      
      func BenchmarkNew(b *testing.B) {
      	for b.Loop() {
      		p := new(123)
      		if p == nil || *p != 123 {
      			b.Fatal()
      		}
      	}
      }
      

      這段代碼需要master分支上的go編譯器才能正常編譯運行,我使用的版本是go1.26-devel_d7a38adf4c

      結果:

      image

      可以看到使用幫助函數要額外多分配一次內存,速度也更慢。這是因為golang的逃逸分析主要保證內存安全,而在優化上比較保守,所以在處理我們的幫助函數時哪怕這個函數已經被內聯,編譯器還是會選擇分配一塊堆內存再返回指向這塊內存的指針。換句話說,編譯器不夠“聰明”。

      但內置函數就不一樣了,內置函數是被編譯器特殊處理的,new會被編譯器改寫:

      p1 := new(int)
      // 改寫成
      // var tmp int
      // p1 := &tmp
      
      p2 := new(12345)
      // 改寫成
      // var tmp int
      // tmp = copy 12345
      // p2 := &tmp
      

      可以看到new是先在當前作用域里創建一個臨時變量,然后再把表達式的結果復制進去的。全程沒有其他的函數調用。

      對于改寫后的代碼,逃逸分析有充足的信息來決定改寫產生的tmp應該分配在棧上還是堆上,比起幫助函數來說獲得了更多的優化機會,因此性能也更好。

      所以官方才有底氣提前宣傳,畢竟不僅解決了痛點,還有額外的收獲。

      總結

      1.26開始內置函數new的參數除了能接受一個類型名稱,現在還可以接收任意的表達式了。

      在新版本中我們可以直接利用內置函數new不需要寫幫助函數了,同時還能收獲更高的性能。

      當然,1.26的新特性開發窗口還沒結束,不能保證最終發布的功能和文章里介紹的一模一樣,但看官方這架勢這個新特性大概率是板上釘釘了,先用這篇文章嘗個鮮也未嘗不可。

      posted @ 2025-09-26 12:04  apocelipes  閱讀(614)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久天天躁夜夜躁狠狠85| 大尺度国产一区二区视频| 亚洲欧洲国产综合aⅴ无码| 午夜精品极品粉嫩国产尤物 | 成年无码av片在线蜜芽| 欧美视频专区一二在线观看| 久久ww精品w免费人成| 日韩精品一区二区三区在| 日韩有码av中文字幕| www国产无套内射com| 绯色蜜臀av一区二区不卡| 亚洲成人av高清在线| 国产成人无码专区| 亚洲一区二区av免费| 老司机免费的精品视频| 性做久久久久久久| 亚洲高清国产自产拍av| 中文字幕av无码免费一区| 亚洲中文字幕无码专区| 亚洲国产性夜夜综合| 久久精品免视看国产成人| 少女韩国在线观看完整版免费| 东京一本一道一二三区| 婷婷色综合成人成人网小说| 亚洲精品国产一区二区三区在线观看 | 国产精品揄拍一区二区久久| 国产午夜亚洲精品久久| 国产二区三区不卡免费| 精品国产亚洲午夜精品a| 中文字幕在线无码一区二区三区| 国产日韩精品中文字幕| 国产精品户外野外| 国产影片AV级毛片特别刺激| 西藏| 水蜜桃av无码| 国产免费一区二区不卡| 欧美成人片一区二区三区| 国产自拍偷拍视频在线观看| 国产不卡av一区二区| 无码乱人伦一区二区亚洲一| 国产一区二区三区精美视频 |