Bagaimana untuk membuat instantiate penuding bukan sifar kepada parameter jenis menggunakan Go generik?

WBOY
Lepaskan: 2024-02-11 17:36:11
ke hadapan
381 orang telah melayarinya

如何使用泛型 Go 实例化类型参数的非零指针?

editor php Xigua akan memperkenalkan kepada anda cara menggunakan penunjuk bukan sifar bagi parameter jenis instantiasi generik dalam bahasa Go. Dalam bahasa Go, generik ialah ciri berkuasa yang boleh meningkatkan fleksibiliti dan kebolehgunaan semula kod. Apabila kita perlu membuat instantiat penunjuk bukan sifar dalam fungsi atau kaedah generik, kita boleh menggunakan penegasan jenis dan refleksi untuk mencapai ini. Dengan menggunakan teknik ini, kita boleh mencipta contoh penuding bukan sifar pada masa jalan berdasarkan jenis parameter jenis tertentu, dengan itu mencapai fleksibiliti dan serba boleh generik. Mari kita lihat dengan lebih dekat kaedah pelaksanaan khusus.

Kandungan soalan

Sekarang parameter jenis tersedia pada golang/go:master, saya memutuskan untuk mencubanya. Nampaknya saya telah mencapai had yang saya tidak dapati dalam cadangan parameter jenis. (Atau saya pasti merinduinya).

Saya ingin menulis fungsi yang mengembalikan sepotong nilai jenis generik dengan kekangan jenis antara muka. Jika jenis yang diluluskan ialah pelaksanaan dengan penerima penuding, bagaimanakah kita hendak membuat instantiat?

type SetGetter[V any] interface {
    Set(V)
    Get() V
}

// SetGetterSlice turns a slice of type V into a slice of type T,
// with T.Set() called for each entry in values.
func SetGetterSlice[V any, T SetGetter[V]](values []V) []T {
    out := make([]T, len(values))

    for i, v := range values {
        out[i].Set(v) // panic if T has pointer receiver!
    }

    return out
}
Salin selepas log masuk

Apabila menggunakan *Count 类型作为 T 调用上述 SetGetterSlice() 函数时,此代码将在调用 Set(v) 时出现混乱。 (Go2go 游乐场)毫不奇怪,因为基本上代码创建了 nil hirisan penunjuk:

// Count implements SetGetter interface
type Count struct {
    x int
}

func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int  { return c.x }

func main() {
    ints := []int{1, 2, 3, 4, 5}

    sgs := SetGetterSlice[int, *Count](ints)
    
    for _, s := range sgs {
        fmt.Println(s.Get())
    }
}
Salin selepas log masuk

Variasi soalan yang sama

Idea ini tidak berfungsi, saya nampaknya tidak dapat mencari cara mudah untuk menyatakan nilai yang ditunjukkan.

  1. out[i] = new(T) 将导致编译失败,因为它返回 *T,其中类型检查器希望查看 T.
  2. Panggil *new(T) 进行编译,但会导致相同的运行时恐慌,因为 new(T) 返回 **Count 在这种情况下,其中指向 Count 的指针仍然是 nil.
  3. Menukar jenis pemulangan kepada serpihan penunjuk kepada T akan menyebabkan kompilasi gagal:
func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T {
    out := make([]*T, len(values))

    for i, v := range values {
        out[i] = new(T)
        out[i].Set(v) // panic if T has pointer receiver
    }

    return out
}

func main() {
    ints := []int{1, 2, 3, 4, 5}

    SetGetterSlice[int, Count](ints)
    // Count does not satisfy SetGetter[V]: wrong method signature
}
Salin selepas log masuk

Penyelesaian

Satu-satunya penyelesaian yang saya temui setakat ini ialah memerlukan penghantaran pembina kepada fungsi generik. Tapi rasa salah dan agak bosan. Jika func F(T interface{})() []T ialah sintaks yang betul-betul sah, mengapakah ia diperlukan?

func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T {
    out := make([]T, len(values))

    for i, v := range values {
        out[i] = constructor()
        out[i].Set(v)
    }

    return out
}

// ...
func main() {
    ints := []int{1, 2, 3, 4, 5}

    SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) })
}
Salin selepas log masuk

Ringkasan

Soalan saya (mengikut keutamaan):

  1. Adakah saya terlepas pandang sesuatu yang jelas?
  2. Adakah ini pengehadan generik dalam Go? Adakah ini sebaik mungkin?
  3. Adakah had ini diketahui atau patutkah saya membangkitkan isu dalam projek Go?

Penyelesaian

Pada asasnya, anda perlu menambah parameter jenis pada kekangan untuk menjadikan T boleh ditukar kepada jenis penunjuknya. Dalam bentuk yang paling asas, teknik ini kelihatan seperti ini (dengan kekangan tanpa nama):

func Foo[T any, PT interface { *T; M() }]() {
    p := PT(new(T))
    p.M() // calling method on non-nil pointer
}
Salin selepas log masuk

Taman permainan:https://www.php.cn/link/24aef8cb3281a2422a59b51659f1ad2e

<小时>

Penyelesaian langkah demi langkah

Kekangan anda SetGetter 已经声明了类型参数 V, jadi kami mengubah suai contoh di atas sedikit:

// V is your original type param
// T is the additional helper param
type SetGetter[V any, T any] interface {
    Set(V)
    Get() V
    *T
}
Salin selepas log masuk

Kemudian tentukan SetGetterSlice 函数,其类型参数为 T any,其目的只是实例化约束 SetGetter.

Anda kemudian boleh menghantar ungkapan &out[i] kepada jenis penunjuk dan berjaya memanggil kaedah pada penerima penunjuk:

// T is the type with methods with pointer receiver
// PT is the SetGetter constraint with *T
func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
    out := make([]T, len(values))

    for i, v := range values {
        // out[i] has type T
        // &out[i] has type *T
        // PT constraint includes *T
        p := PT(&out[i]) // valid conversion!
        p.Set(v)         // calling with non-nil pointer receiver
    }

    return out
}
Salin selepas log masuk

Program penuh:

CFE57E536C89530D9A8C38E10967A10D

Ini menjadi lebih bertele-tele kerana SetGetterSlice kini memerlukan tiga jenis parameter: V tambah SetGetterSlice 现在需要三个类型参数:原始 V 加上 T (带有指针接收器的类型)和 PT (新约束)。然而,当您调用该函数时,您可以省略第三个 - 通过类型推断,实例化 PT SetGetter[V,T] 所需的类型参数 VT (jenis dengan penerima penunjuk) dan PT< /code> ( kekangan baru). Walau bagaimanapun, apabila anda memanggil fungsi tersebut, anda boleh meninggalkan yang ketiga - melalui jenis inferens, parameter jenis <code>V yang diperlukan untuk membuat instantiate PT SetGetter[V,T] dan

Itu sahaja dikenali:

SetGetterSlice[int, Count](ints)
Salin selepas log masuk
Taman permainan:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1🎜

Atas ialah kandungan terperinci Bagaimana untuk membuat instantiate penuding bukan sifar kepada parameter jenis menggunakan Go generik?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!