제네릭을 사용하여 Go에서 플래그 작업

19057 단어 gotutorialprogramming

GO-상태



개요



  • GO-STATUS
  • OUTLINE
  • The Issue
  • The Setup
  • The Tests
  • The Implementation
  • Test Output
  • Installation
  • Conclusion


  • 문제



    Go에서 작은 상태 머신을 작성하는 동안 코드를 반복하지 않고는 열거형에 플래그 작업을 구현하는 명확한 방법을 찾을 수 없다는 점이 저를 괴롭혔습니다. 불과 몇 달 전에 Go를 사용하기 시작한 경험 부족과 관련이 있을 수 있지만 Go 버전 1.18부터 사용할 수 있는 제네릭 기능을 사용하여 "깨끗한"것으로 간주할 수 있는 방법을 찾았습니다. 이 게시물에서 관심 있는 분들과 기꺼이 공유하겠습니다.

    설정



    다음 열거형을 고려하십시오.

    package status
    
    // MyStatus is an enumeration that indicates my liveliness
    type MyStatus int
    
    const (
        Unknown MyStatus = 1 << iota
        Born    MyStatus = 2
        Living  MyStatus = 4
        Dead    MyStatus = 8
    )
    
    var me MyStatus
    
    func init() {
        me = Unknown
    }
    


    테스트




    package status
    
    import (
        "github.com/stretchr/testify/assert"
        "testing"
    )
    
    func TestThatICanBeBorn(t *testing.T) {
        // GIVEN
        me = Unknown
        assert.True(t, HasStatus(me, Unknown))
        // WHEN
        ForceStatus(&me, Born)
        // THEN
        assert.True(t, HasStatus(me, Born))
        assert.True(t, NotHasStatus(me, Unknown))
    }
    
    func TestThatICanDie(t *testing.T) {
        // GIVEN
        me = Unknown
        assert.True(t, HasStatus(me, Unknown))
        // WHEN
        SetStatus(&me, Born)
        assert.True(t, HasStatus(me, Born))
        // AND
        SetStatus(&me, Living)
        assert.True(t, HasStatus(me, Living))
        // AND
        UnsetStatus(&me, Born)
        assert.True(t, NotHasStatus(me, Born))
        // AND
        ForceStatus(&me, Dead)
        // THEN
        assert.True(t, NotHasStatus(me, Born))
        assert.True(t, NotHasStatus(me, Living))
        assert.True(t, HasStatus(me, Dead))
    }
    
    func TestThatWeCanSetAndUnsetMultipleFlagsAtOnce(t *testing.T) {
        // GIVEN
        me = Unknown
        assert.True(t, HasStatus(me, Unknown))
        // WHEN
        SetMulti(&me, Born, Living, Dead)
        // THEN
        assert.True(t, HasMulti(me, Born, Living, Dead))
        // AND WHEN
        UnsetMulti(&me, Born, Dead)
        assert.True(t, !HasMulti(me, Born, Dead))
    
    }
    


    구현




    package status
    
    // HasStatus accepts an input status and will check if the flag is set using bitwise OR and return true if the flag is set.
    func HasStatus[T ~int](status T, flag T) bool {
        return status|flag == status
    }
    
    // NotHasStatus accepts an input status and will check if the flag is not set.
    func NotHasStatus[T ~int](status T, flag T) bool {
        return !HasStatus(status, flag)
    }
    
    // UnsetStatus accepts an input status reference and will unset the flag using bitwise AND NOT.
    func UnsetStatus[T ~int](status *T, flag T) {
        *status = *status &^ flag
    }
    
    // ForceStatus accepts an input status reference and will simply overwrite the status with the flag.
    func ForceStatus[T ~int](status *T, flag T) {
        *status = flag
    }
    
    // SetStatus accepts an input status reference and will set the flag to the status using bitwise OR.
    func SetStatus[T ~int](status *T, flag T) {
        *status = *status | flag
    }
    
    // SetMulti allows you to set multiple flags at once
    func SetMulti[T ~int](status *T, flags ...T) {
        if len(flags) == 0 {
            return
        }
        for _, flag := range flags {
            SetStatus[T](status, flag)
        }
    }
    
    // HasMulti allows you to check for many flags at once.
    func HasMulti[T ~int](status T, flags ...T) bool {
        if len(flags) == 0 {
            return false
        }
        for _, flag := range flags {
            if NotHasStatus[T](status, flag) {
                return false
            }
        }
        return true
    }
    
    // UnsetMulti allows you to clear multiple flags at once.
    func UnsetMulti[T ~int](status *T, flags ...T) {
        if len(flags) == 0 {
            return
        }
        for _, flag := range flags {
            UnsetStatus[T](status, flag)
        }
    }
    


    테스트 출력




    /usr/local/go/bin/go test -json ./...
    === RUN   TestThatICanBeBorn
    --- PASS: TestThatICanBeBorn (0.00s)
    === RUN   TestThatICanDie
    --- PASS: TestThatICanDie (0.00s)
    === RUN   TestWeCanSetAndUnsetMultipleFlagsAtOnce
    --- PASS: TestWeCanSetAndUnsetMultipleFlagsAtOnce (0.00s)
    PASS
    ok      <REDACTED>
    0.003s
    
    Process finished with the exit code 0
    


    설치




    go get github.com/discomco/go-status
    


    결론



    나는 이것이 Go에서 Flags로 작업하는 깔끔한 방법이라고 생각합니다. 물론 저는 아직 이 언어를 처음 접했고 이 모든 것을 구식으로 만들 내장 기능을 놓쳤을 수도 있습니다. 저에게는 Go 제네릭의 메커니즘을 조금 더 잘 배웠습니다. 이러한 메서드를 호출하는 데 명시적 형식 추론이 필요하지 않은 것 같습니다.
    전문가의 의견과 더 많은 것을 배울 수 있는 기회를 기대합니다!

    좋은 웹페이지 즐겨찾기