博客 / 詳情

返回

Go:一種很“新”的類型斷言

分享組件:go-kratos-ecosystem/components

注:該功能計劃在 v2.11.0 版本中發佈。

原文:https://flc.io/go-is-type/

引言

我們在早期使用 Go 做類型斷言的時候,大多是這麼用:

var v interface{}
v = 10

if _, ok := v.(int); ok {
    fmt.Println("v is int")
}

那現在,我們只需要這麼用:

if IsType[int](v) {
    fmt.Println("v is int")
}

主角

這就是今天要給大家分享的一個新方法:IsType。這個想法來自於 關於增加 is[T any](any) bool 的提案 。

我們先看看實際的代碼和單測效果。

  • 代碼:

    func IsType[T any](value any) bool {
        _, ok := value.(T)
        return ok
    }
  • 單測:

    func TestIsType(t *testing.T) {
        assert.True(t, IsType[int](10))
        assert.False(t, IsType[int](int8(10)))
    
        assert.True(t, IsType[string]("foo"))
        assert.False(t, IsType[string](10))
    
        assert.True(t, IsType[time.Time](time.Now()))
        assert.False(t, IsType[time.Time](10))
    
        assert.True(t, IsType[foo](foo{}))
        assert.False(t, IsType[foo](10))
    
        assert.True(t, IsType[*foo](&foo{}))
        assert.False(t, IsType[foo](&foo{}))
        assert.False(t, IsType[*foo](nil))
    
        assert.True(t, IsType[testInterface](testStruct{}))
        assert.True(t, IsType[interface{}](testStruct{}))
        assert.True(t, IsType[any](testStruct{}))
    
        assert.True(t, IsType[error](errors.New("foo")))
        assert.False(t, IsType[error](nil))
    }
  • 單測結果:

    === RUN   TestIsType
    --- PASS: TestIsType (0.00s)
    PASS

底層其實就是應用了 Go 1.18+ 中的泛型特性,通過這種方式,我們可以很方便地判斷一個值的類型。原來三行代碼,現在只需要一行代碼就可以搞定。

// 之前
var isType bool
if _, ok := v.(int); ok {
    isType = true
}

// 但現在
isType = IsType[int](v)

性能

考慮到使用了泛型,我們來比較下使用原生和使用泛型的性能損耗。

相關腳本:https://github.com/go-kratos-ecosystem/components/pull/185/files
  • 原生:

    單測腳本:

    func testIsType(value any) bool {
        _, ok := value.(int)
        return ok
    }
    
    func BenchmarkIsType_Native(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
            for pb.Next() {
                testIsType(10)
            }
        })
    }

    單測結果:

    BenchmarkIsType_Native
    BenchmarkIsType_Native-8       1000000000             0.2330 ns/op
  • 泛型:

    單測腳本:

    func BenchmarkIsType_Generics(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
            for pb.Next() {
                IsType[int](10)
            }
        })
    }

    單測結果:

    BenchmarkIsType_Generics
    BenchmarkIsType_Generics-8       1000000000             0.2403 ns/op

可以看到,性能損耗幾乎可以忽略不計。

應用

實際應用中,類似很多語言的 instanceof 操作,我們可以使用這種方式來判斷類型。

type Named interface {
    Name() string
}

type Person struct {
    Name string
}

func (p Person) Name() string {
    return p.Name
}

func main() {
    var n Named = Person{"foo"}
    if IsType[Person](n) {
        fmt.Println(n.Name())
    }
}

參考資料

  • 關於增加 is[T any](any) bool 的提案
  • Go 1.18 泛型
  • Go Kratos Components
  • values.IsType
  • 性能測試腳本
  • PR: Add IsType
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.