最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang中一種不常見的switch語句寫法示例詳解

 更新時間:2023年05月03日 09:50:34   作者:apocelipes  
這篇文章主要介紹了golang中一種不常見的switch語句寫法,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

最近翻開源代碼的時候看到了一種很有意思的switch用法,分享一下。

注意這里討論的不是typed switch,也就是case語句后面是類型的那種。

直接看代碼:

func (s *systemd) Status() (Status, error) {
	exitCode, out, err := s.runWithOutput("systemctl", "is-active", s.unitName())
	if exitCode == 0 && err != nil {
		return StatusUnknown, err
	}
	switch {
	case strings.HasPrefix(out, "active"):
		return StatusRunning, nil
	case strings.HasPrefix(out, "inactive"):
		// inactive can also mean its not installed, check unit files
		exitCode, out, err := s.runWithOutput("systemctl", "list-unit-files", "-t", "service", s.unitName())
		if exitCode == 0 && err != nil {
			return StatusUnknown, err
		}
		if strings.Contains(out, s.Name) {
			// unit file exists, installed but not running
			return StatusStopped, nil
		}
		// no unit file
		return StatusUnknown, ErrNotInstalled
	case strings.HasPrefix(out, "activating"):
		return StatusRunning, nil
	case strings.HasPrefix(out, "failed"):
		return StatusUnknown, errors.New("service in failed state")
	default:
		return StatusUnknown, ErrNotInstalled
	}
}

你也可以在這找到它:代碼鏈接

簡單解釋下這段代碼在做什么:調(diào)用systemctl命令檢查指定的服務的運行狀態(tài),具體做法是過濾systemctl的輸出然后根據(jù)得到的字符串的前綴判斷當前的運行狀態(tài)。

有意思的在于這個switch,首先它后面沒有任何表達式;其次在每個case后面都是個函數(shù)調(diào)用表達式,返回值都是bool類型的。

雖然看起來很怪異,但這段代碼肯定沒有語法問題,可以編譯通過;也沒有語義或者邏輯問題,因為人家用的好好的,這個項目接近4000個星星不是大家亂點的。

這里就不賣關子了,直接公布答案:

如果switch后面沒有任何表達式,那么它等價于這個:switch true;case表達式按從上到下從左到右的順序求值;如果case后面的表達式求出來的值和switch后面的表達式的值一樣,那么就進入這個分支,其他case被忽略(除非用了fallthrough,但這會直接跳進下一個case的分支,不會執(zhí)行下一個case上的表達式)。

那么上面那一串代碼就好理解了:

首先是switch true,期待有個case能求出true這個值;從上到下執(zhí)行strings.HasPrefix,如果是false就往下到下一個case,如果是true就進入這個case的分支。

它等價于下面這段:

func (s *systemd) Status() (Status, error) {
	exitCode, out, err := s.runWithOutput("systemctl", "is-active", s.unitName())
	if exitCode == 0 && err != nil {
		return StatusUnknown, err
	}
    if strings.HasPrefix(out, "active") {
        return StatusRunning, nil
    }
    if strings.HasPrefix(out, "inactive") {
        // inactive can also mean its not installed, check unit files
		exitCode, out, err := s.runWithOutput("systemctl", "list-unit-files", "-t", "service", s.unitName())
		if exitCode == 0 && err != nil {
			return StatusUnknown, err
		}
		if strings.Contains(out, s.Name) {
			// unit file exists, installed but not running
			return StatusStopped, nil
		}
		// no unit file
		return StatusUnknown, ErrNotInstalled
    }
    if strings.HasPrefix(out, "activating") {
		return StatusRunning, nil
    }
    if strings.HasPrefix(out, "failed") {
        return StatusUnknown, errors.New("service in failed state")
    }
	return StatusUnknown, ErrNotInstalled
}

可以看到,光從可讀性上來說的話兩者很難說誰更優(yōu)秀;兩者同樣需要注意把常見的情況放在最前面來減少不必要的匹配(這里的switch-case不能像給整數(shù)常量時那樣直接進行跳轉,實際執(zhí)行和上面給出的if語句是差不多的)。

那么我們再來看看兩者的生成代碼,通常我不喜歡去研究編譯器生成的代碼,但這次是個小例外,對于執(zhí)行流程上很接近的兩段代碼,編譯器會怎么處理呢?

我們做個簡化版的例子:

func status1(cmdOutput string, flag int) int {
    switch {
    case strings.HasPrefix(cmdOutput, "active"):
        return 1
    case strings.HasPrefix(cmdOutput, "inactive"):
        if flag > 0 {
            return 2
        }
        return -1
    case strings.HasPrefix(cmdOutput, "activating"):
        return 1
    case strings.HasPrefix(cmdOutput, "failed"):
        return -1
    default:
        return -2
    }
}
func status2(cmdOutput string, flag int) int {
    if strings.HasPrefix(cmdOutput, "active") {
        return 1
    }
    if strings.HasPrefix(cmdOutput, "inactive") {
        if flag > 0 {
            return 2
        }
        return -1
    }
    if strings.HasPrefix(cmdOutput, "activating") {
        return 1
    }
    if strings.HasPrefix(cmdOutput, "failed") {
        return -1
    }
    return -2
}

這是switch版本的匯編:

main_status1_pc0:
        TEXT    main.status1(SB), ABIInternal, $40-24
        CMPQ    SP, 16(R14)
        PCDATA  $0, $-2
        JLS     main_status1_pc273
        PCDATA  $0, $-1
        SUBQ    $40, SP
        MOVQ    BP, 32(SP)
        LEAQ    32(SP), BP
        FUNCDATA        $0, gclocals·wgcWObbY2HYnK2SU/U22lA==(SB)
        FUNCDATA        $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        FUNCDATA        $5, main.status1.arginfo1(SB)
        FUNCDATA        $6, main.status1.argliveinfo(SB)
        PCDATA  $3, $1
        MOVQ    CX, main.flag+64(SP)
        MOVQ    AX, main.cmdOutput+48(SP)
        MOVQ    BX, main.cmdOutput+56(SP)
        PCDATA  $3, $-1
        MOVL    $6, DI
        LEAQ    go:string."active"(SB), CX
        PCDATA  $1, $0
        CALL    strings.HasPrefix(SB)
        NOP
        TESTB   AL, AL
        JNE     main_status1_pc258
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."inactive"(SB), CX
        MOVL    $8, DI
        NOP
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JEQ     main_status1_pc147
        MOVQ    main.flag+64(SP), CX
        TESTQ   CX, CX
        JLE     main_status1_pc130
        MOVL    $2, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc130:
        MOVQ    $-1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc147:
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."activating"(SB), CX
        MOVL    $10, DI
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JNE     main_status1_pc243
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."failed"(SB), CX
        MOVL    $6, DI
        PCDATA  $1, $1
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JEQ     main_status1_pc226
        MOVQ    $-1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc226:
        MOVQ    $-2, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc243:
        MOVL    $1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc258:
        MOVL    $1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status1_pc273:
        NOP
        PCDATA  $1, $-1
        PCDATA  $0, $-2
        MOVQ    AX, 8(SP)
        MOVQ    BX, 16(SP)
        MOVQ    CX, 24(SP)
        CALL    runtime.morestack_noctxt(SB)
        MOVQ    8(SP), AX
        MOVQ    16(SP), BX
        MOVQ    24(SP), CX
        PCDATA  $0, $-1
        JMP     main_status1_pc0

我把inline給關了,不然hasprefix內(nèi)聯(lián)出來的東西會導致整個匯編代碼難以閱讀。

上面的代碼還是很好理解的,“active”和“inactive”的case被放在一起,如果匹配到了就跳轉進入對應的分支;“activing”和“failed”的case也放在了一起,匹配到之后的操作與前面兩個case一樣(實際上上面兩個case的匹配執(zhí)行完就會跳轉到這兩個,至于為啥要多一次跳轉我沒深究,可能是為了提高L1d的命中率,一大塊指令可能會導致緩存里放不下從而付出更新緩存的代價,而有流水線優(yōu)化的情況下一個jmp帶來的開銷可能低于緩存未命中的懲罰,不過這在實踐里很難測量,權當我在自言自語也行)。最后那一串帶ret的語句塊就是對應的case的分支。

再來看看if的代碼:

main_status2_pc0:
        TEXT    main.status2(SB), ABIInternal, $40-24
        CMPQ    SP, 16(R14)
        PCDATA  $0, $-2
        JLS     main_status2_pc273
        PCDATA  $0, $-1
        SUBQ    $40, SP
        MOVQ    BP, 32(SP)
        LEAQ    32(SP), BP
        FUNCDATA        $0, gclocals·wgcWObbY2HYnK2SU/U22lA==(SB)
        FUNCDATA        $1, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        FUNCDATA        $5, main.status2.arginfo1(SB)
        FUNCDATA        $6, main.status2.argliveinfo(SB)
        PCDATA  $3, $1
        MOVQ    CX, main.flag+64(SP)
        MOVQ    AX, main.cmdOutput+48(SP)
        MOVQ    BX, main.cmdOutput+56(SP)
        PCDATA  $3, $-1
        MOVL    $6, DI
        LEAQ    go:string."active"(SB), CX
        PCDATA  $1, $0
        CALL    strings.HasPrefix(SB)
        NOP
        TESTB   AL, AL
        JNE     main_status2_pc258
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."inactive"(SB), CX
        MOVL    $8, DI
        NOP
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JEQ     main_status2_pc147
        MOVQ    main.flag+64(SP), CX
        TESTQ   CX, CX
        JLE     main_status2_pc130
        MOVL    $2, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc130:
        MOVQ    $-1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc147:
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."activating"(SB), CX
        MOVL    $10, DI
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JNE     main_status2_pc243
        MOVQ    main.cmdOutput+48(SP), AX
        MOVQ    main.cmdOutput+56(SP), BX
        LEAQ    go:string."failed"(SB), CX
        MOVL    $6, DI
        PCDATA  $1, $1
        CALL    strings.HasPrefix(SB)
        TESTB   AL, AL
        JEQ     main_status2_pc226
        MOVQ    $-1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc226:
        MOVQ    $-2, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc243:
        MOVL    $1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc258:
        MOVL    $1, AX
        MOVQ    32(SP), BP
        ADDQ    $40, SP
        RET
main_status2_pc273:
        NOP
        PCDATA  $1, $-1
        PCDATA  $0, $-2
        MOVQ    AX, 8(SP)
        MOVQ    BX, 16(SP)
        MOVQ    CX, 24(SP)
        CALL    runtime.morestack_noctxt(SB)
        MOVQ    8(SP), AX
        MOVQ    16(SP), BX
        MOVQ    24(SP), CX
        PCDATA  $0, $-1
        JMP     main_status2_pc0

除了函數(shù)名子不一樣之外,其他是一模一樣的,可以說兩者在生成代碼上也沒有區(qū)別。

你可以在這里看到代碼和他們的編譯產(chǎn)物:Compiler Explorer

既然生成代碼是一樣的,那性能就沒必要測量了,因為肯定是一樣的。

最后總結一下這種不常用的switch寫法,形式如下:

switch {
case 表達式1: // 如果是true
    do works1
case 表達式2: // 如果是true
    do works2
default:
    都不是true就會到這里
}

考慮到在性能上這并沒有什么優(yōu)勢,而且對于初次見到這個寫法的人可能不能很快理解它的含義,所以這個寫法的使用場景我目前能想到的只有一處:

如果你的數(shù)據(jù)有固定的2種以上的前綴/后綴/某種模式,因為沒法用固定的常量去表示這種情況,那么用case加上一個簡單的表達式(函數(shù)調(diào)用之類的)會比用if更緊湊,也能更好地表達語義,case越多效果越明顯。比如我在開頭舉的那個例子。

如果你的代碼不符合上述情況,那還是老老實實用if會更好。

話說回來,雖然你機會沒啥機會寫出這種switch語句,但最好還是得看懂,不然下回看見它就只能干瞪眼了。

參考

https://go.dev/ref/spec#Switch_statements

到此這篇關于golang中一種不常見的switch語句寫法的文章就介紹到這了,更多相關golang switch語句寫法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go語言動態(tài)并發(fā)控制sync.WaitGroup的靈活運用示例詳解

    Go語言動態(tài)并發(fā)控制sync.WaitGroup的靈活運用示例詳解

    本文將講解 sync.WaitGroup 的使用方法、原理以及在實際項目中的應用場景,用清晰的代碼示例和詳細的注釋,助力讀者掌握并發(fā)編程中等待組的使用技巧
    2023-11-11
  • Go語言制作svg格式樹形圖的示例代碼

    Go語言制作svg格式樹形圖的示例代碼

    SVG是可伸縮矢量圖形?(Scalable?Vector?Graphics),于2003年1月14日成為?W3C?推薦標準。本文將利用Go語言實現(xiàn)制作svg格式樹形圖,感興趣的可以了解一下
    2022-09-09
  • 使用gin框架搭建簡易服務的實現(xiàn)方法

    使用gin框架搭建簡易服務的實現(xiàn)方法

    go語言web框架挺多的,本文就介紹了一下如何使用gin框架搭建簡易服務的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang中判斷請求是http還是https獲取當前訪問地址

    golang中判斷請求是http還是https獲取當前訪問地址

    這篇文章主要為大家介紹了golang中判斷請求是http還是https獲取當前訪問地址示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • 詳解如何在Golang中實現(xiàn)CORS(跨域)

    詳解如何在Golang中實現(xiàn)CORS(跨域)

    很多時候,需要允許Web應用程序在不同域之間(跨域)實現(xiàn)共享資源,本文將簡介跨域、CORS的概念,以及如何在Golang中如何實現(xiàn)CORS,文中有詳細的示例代碼,需要的朋友可以參考下
    2023-10-10
  • 在golang xorm中使用postgresql的json,array類型的操作

    在golang xorm中使用postgresql的json,array類型的操作

    這篇文章主要介紹了在golang xorm中使用postgresql的json,array類型的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang 串口通信的實現(xiàn)示例

    Golang 串口通信的實現(xiàn)示例

    串口通信是一種常見的硬件通信方式,用于在計算機和外部設備之間傳輸數(shù)據(jù),本文主要介紹了Golang 串口通信的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • golang結構化日志log/slog包之LogValuer的用法簡介

    golang結構化日志log/slog包之LogValuer的用法簡介

    這篇文章主要為大家詳細介紹了golang結構化日志log/slog包中 LogValuer 和日志記錄函數(shù)的正確包裝方法,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-10-10
  • go mod 依賴管理的具體使用

    go mod 依賴管理的具體使用

    在Go語言開發(fā)中,依賴管理是一項非常重要的工作,Go mod作為官方的包管理工具已經(jīng)成為了Go語言依賴管理的首選方式,本文就來介紹一下go mod 依賴管理的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Golang實現(xiàn)協(xié)程超時控制的方式總結

    Golang實現(xiàn)協(xié)程超時控制的方式總結

    我們知道,go協(xié)程如果不做好處理,很容易造成內(nèi)存泄漏,所以對goroutine做超時控制,才能有效避免這種情況發(fā)生,本文為大家整理了兩個常見的Golang超時控制方法,需要的可以收藏一下
    2023-05-05

最新評論

元氏县| 长垣县| 包头市| 固原市| 通渭县| 辽中县| 千阳县| 拉孜县| 灵璧县| 三亚市| 静安区| 蓝山县| 施甸县| 西乌珠穆沁旗| 章丘市| 石河子市| 彰化市| 义乌市| 武汉市| 湘阴县| 通城县| 内江市| 颍上县| 西林县| 越西县| 安庆市| 麻江县| 榆社县| 桐乡市| 灌阳县| 台前县| 扎鲁特旗| 开封市| 乌苏市| 灌阳县| 铜山县| 正定县| 永善县| 浠水县| 永定县| 赞皇县|