自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于Go錯(cuò)誤處理新提案的一個(gè)想法:?操作符這樣用行不行

開發(fā) 前端
Ian Taylor在discussion中明確了該提案的目標(biāo)是引入一種新語法,在不影響控制流清晰度的前提下,減少正常情況下檢查錯(cuò)誤所需的代碼量。

0. 背景

Ian Taylor在關(guān)閉了旨在消除Go錯(cuò)誤處理樣板代碼的issue[1]之后,又另起了一個(gè)“同名”的discussion[2]。錯(cuò)誤處理真不愧是Go社區(qū)呼聲最高的問題,幾天之內(nèi)又收到了近500條回復(fù)!不過到目前為止,依然沒有形成統(tǒng)一和高贊的意見。

關(guān)于error handling的樣板代碼過多,其實(shí)我個(gè)人是可以接受的,即便不做出任何改變也是ok的,估計(jì)Go社區(qū)與我有相同看法的也不在少數(shù)。比如就有人引用了Rob Pike的權(quán)威觀點(diǎn)[3],并認(rèn)為Go應(yīng)該按照Rob大神的思路,保持Go語法穩(wěn)定:  

圖片

  不過自然也會有另外一批人強(qiáng)烈希望錯(cuò)誤處理的樣板代碼得到改進(jìn)。

Ian Taylor在discussion中明確了該提案的目標(biāo)是引入一種新語法,在不影響控制流清晰度的前提下,減少正常情況下檢查錯(cuò)誤所需的代碼量。  

 Ian最初的Proposal由于隱式聲明變量err以及可選代碼塊等問題而備受“批評”,并且似乎該proposal違反了他自己提出的目標(biāo)。  

今天在discussion中看到一位名為Mukunda Johnson的gopher的評論[4],我覺得很有道理。其核心觀點(diǎn)就是:盡量保持Go的傳統(tǒng)語法形式。他還給出了期望中的語法示例:

// 當(dāng)前錯(cuò)誤處理樣板代碼過多的示例
f, err := open(file)
if err != nil {
   return err
}
defer f.Close()

if err = binwrite(f, signature); err != nil {
   return err
}
if err = binwrite(f, header); err != nil {
   return err
}
if err = binwrite(f, zeroSegment); err != nil {
   return err
}

for _, s := range segments {
   if err = binwrite(f, s); err != nil {
      return err
   }
}

if err = binwrite(f, footer); err != nil {
   return err
}

vs. 

// 使用新語法改進(jìn)后的代碼
f, err := open(file)?
defer f.Close()

binwrite(f, signature)?
binwrite(f, header)?
binwrite(f, zeroSegment)?

for _, s := range segments {
   binwrite(f, s)?
}

binwrite(f, footer)?

這給了我很大啟發(fā):我們可以引入?語法,但是如果結(jié)合原先err變量的聲明形式豈不是更好!比如:

f, err := open(file)?

豈不是要比下面兩種形式更好!

f := open(file)?

或

f := open(file)? err { }

通過僅引入一個(gè)問號(?)操作符,并避免引入過多的新語法形式,卻能解決60%的錯(cuò)誤處理樣板問題。根據(jù)jba對Go開源代碼中錯(cuò)誤處理的抽樣統(tǒng)計(jì)[5],超過60%的錯(cuò)誤處理都是直接返回err,而沒有對err進(jìn)行任何修飾。此外,顯式聲明err可以最大程度地避免隱式聲明帶來的問題,同時(shí)提升代碼的可讀性。

因此,基于盡量使用已有Go代碼風(fēng)格、最大程度避免隱式聲明,并僅解決最常見的錯(cuò)誤處理樣板代碼的原則,下面我基于Ian提案的錯(cuò)誤處理改進(jìn)語法,談點(diǎn)自己關(guān)于新?操作符使用的想法,大家看看是否可行。

1. 對于最常見的未經(jīng)修飾的錯(cuò)誤處理代碼

err := SomeFunction2()
if err != nil {
 return err
}

或是

if err := SomeFunction2(); err != nil {
    return err
}

我們使用下面的新語法做等價(jià)替代:

err := SomeFunction2() ?

如果聲明的錯(cuò)誤變量名為err,也可省略賦值操作符左側(cè)代碼,從而簡化為:

SomeFunction2() ? // 這里略帶隱式

2. 如果函數(shù)返回值有多個(gè),甚至有多個(gè)錯(cuò)誤變量的情況

比如下面代碼:

a, b, err0, err1, err2 := SomeFunction3()
if err2 != nil {
    return err2
}

我們可以將其改寫為:

a, b, err0, err1, err2 := SomeFunction3()?

其語義是如果err2不為nil,返回err2,但前提要保證賦值語句的左側(cè)的最后一個(gè)變量err2必須是實(shí)現(xiàn)error接口的類型的變量。

如果是像下面這樣在err2 != nil時(shí)有多個(gè)返回值,又該如何處理呢?

a, b, err0, err1, err2 := SomeFunction3()
if err2 != nil {
    return a, b, err2
}

對于這種情況,我認(rèn)為可以不在新方案的考慮范圍之內(nèi),現(xiàn)在怎么寫,請繼續(xù)這么寫。如果非要解決,請繼續(xù)看后面支持可選代碼塊的情況。

實(shí)現(xiàn)以上兩種情況,就能解決60%以上的錯(cuò)誤樣板代碼問題了!

3. 對于對返回的error值進(jìn)行修飾的情況

對于像下面兩種對返回的error變量進(jìn)行修飾的情況:

r, err := SomeFunction()
if err != nil {
    return fmt.Errorf("something failed: %v", err)
}

if err := SomeFunction2(); err != nil {
    return fmt.Errorf("something else failed: %v", err)
}

我的第一想法是保持現(xiàn)狀 ,不在新方案考慮范圍之內(nèi)。

不過如果非要在新方案中解決,那就需要引入可選代碼塊(optional block)了!比如:

r, err := SomeFunction() ? {
    return fmt.Errorf("something failed: %v", err)
}

err := SomeFunction2() ? {
    return fmt.Errorf("something else failed: %v", err)
}

和Ian的原proposal中語法不同,這里我們依然顯式聲明了err,當(dāng)然你也可以不用err這個(gè)名字,由于是顯式聲明,你用任何名字均可,比如:

r, e := SomeFunction() ? {
    return fmt.Errorf("something failed: %v", e)
}

myErr := SomeFunction2() ? {
    return fmt.Errorf("something else failed: %v", myErr)
}

這將避免隱式聲明帶來的諸多問題!

基于可選代碼塊,我們也可以處理一下前面提到的返回多個(gè)值的情況。下面代碼

a, b, err0, err1, err2 := SomeFunction3()
if err2 != nil {
    return a, b, err2
}

可以改寫為:

a, b, err0, err1, err2 := SomeFunction3() ? {
    return a, b, err2
}

這里加入可選代碼塊后,我建議開發(fā)人員負(fù)責(zé)顯式調(diào)用return,而不是由?操作符來自動return,也就是說完全將控制權(quán)交給你。如果你沒有在可選代碼塊中調(diào)用return,那么代碼在執(zhí)行完可選代碼塊中的代碼后,還會繼續(xù)向下執(zhí)行??蛇x代碼塊相當(dāng)于一個(gè)error handler,而不帶可選代碼塊的情況,默認(rèn)的error handler其實(shí)就是一個(gè)return err,偽代碼類似這樣:

err := SomeFunction2() ?

<=>

err := SomeFunction2() ? {
 return err
}

這樣解釋后,你是不是覺得在語義層面,不帶可選代碼塊與帶有可選代碼塊的情況就統(tǒng)一和一致了呢!

本質(zhì)上來說,?+可選代碼塊僅是讓你少敲了個(gè)if以及err != nil。

4. 綜合示例

Mukunda Johnson給出的示例其實(shí)已經(jīng)可以很好地展示?操作符+顯式聲明err方案帶來的消除樣板代碼的效果,這里再回顧一下(這里沒用到可選代碼塊,因此代碼顯得格外清晰):

f, err := open(file)?
defer f.Close()

binwrite(f, signature)?
binwrite(f, header)?
binwrite(f, zeroSegment)?

for _, s := range segments {
   binwrite(f, s)?
}

binwrite(f, footer)?

此外,在原discussion中,另外一個(gè)gopher提出的示例,我們也可以用上面的想法改寫一下:

// 最常見的情況
SomeFunc() ?

// 多個(gè)返回值,最后一個(gè)為error變量
a, err1 := SomeFunction2() ?

// 返回前對err進(jìn)行修飾
err := SomeFunc() ? {
  return fmt.Errorf("oh no: %w", err)
}

// 顯式聲明避免變量遮蔽
err := SomeFunc() ?  {
  otherErr := OtherFunc() ?  {
    err = errors.Wrap(err, otherErr) // 在可選代碼塊中沒有顯式調(diào)用return,代碼還會繼續(xù)向后執(zhí)行
  }
  return fmt.Errorf("oh no: %w", err)
}

5. 小結(jié)

再來簡單總結(jié)一下上面想法中的語法形式的優(yōu)勢:

  • 與傳統(tǒng)Go語法形式幾乎一致,盡量避免引入過多新語法形式,在不使用可選代碼塊的時(shí)候,只是多了一個(gè)問號(?)。
  • 顯式聲明err變量,最大程度避免隱式聲明帶來的問題。
  • 專注解決最常見的錯(cuò)誤處理樣板情景,其他場景保持當(dāng)前寫法即可。
  • 即便引入可選代碼塊,本質(zhì)上與不用可選代碼塊的語法在語義層面也是統(tǒng)一和一致的。

這一語法方案保留了原Ian提案中的優(yōu)勢,并能消除一些缺點(diǎn),如變量遮蔽和隱式聲明等。不過,仍然有些原proposal中的劣勢問題無法完全消除,但這些問題顯然不是主要關(guān)注點(diǎn)。

需要注意的是,以上想法目前僅停留在形式討論層面,技術(shù)層面是否可行尚不確定。

大家認(rèn)為我的想法可行嗎?希望大家能提出更具建設(shè)性的意見^_^。

參考資料

[1] 旨在消除Go錯(cuò)誤處理樣板代碼的issue: https://github.com/golang/go/issues/71203

[2] discussion: https://github.com/golang/go/discussions/71460

[3] Rob Pike的權(quán)威觀點(diǎn): https://go.dev/talks/2015/simplicity-is-complicated.slide#9

[4] Mukunda Johnson的gopher的評論: https://github.com/golang/go/discussions/71460#discussioncomment-12084482

[5] jba對Go開源代碼中錯(cuò)誤處理的抽樣統(tǒng)計(jì): https://github.com/golang/go/issues/71203#issuecomment-2585915103

責(zé)任編輯:武曉燕 來源: TonyBai
相關(guān)推薦

2024-06-05 08:47:20

Go語言方式

2022-07-13 08:53:28

函數(shù)Go語言

2022-09-05 08:55:15

Go2提案語法

2022-10-24 08:55:13

Go工具鏈開發(fā)者

2025-02-24 09:30:15

2021-09-27 10:04:03

Go程序處理

2021-09-27 15:33:48

Go 開發(fā)技術(shù)

2024-11-19 09:10:19

迭代器Go語言

2014-11-17 10:05:12

Go語言

2021-04-29 09:02:44

語言Go 處理

2021-09-13 07:53:31

Go錯(cuò)誤處理

2020-12-17 06:25:05

Gopanic 模式

2023-03-10 08:48:29

2020-08-20 10:16:56

Golang錯(cuò)誤處理數(shù)據(jù)

2022-12-12 08:53:53

Go版本方式

2024-02-28 08:54:57

switchGo錯(cuò)誤

2013-03-18 10:31:22

JS異常

2020-03-03 15:42:33

Python字典合并代碼

2025-03-31 00:29:44

2021-04-15 08:55:51

Go struc代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號