Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Jenis dinamik tidak dikosongkan semasa menetapkan semula pembolehubah - adakah ini pepijat?

Jenis dinamik tidak dikosongkan semasa menetapkan semula pembolehubah - adakah ini pepijat?

PHPz
PHPzke hadapan
2024-02-13 16:00:11446semak imbas

重新分配变量时动态类型未清除 - 这是一个错误吗?

Editor PHP berada di sini untuk menjawab soalan biasa: "Jenis dinamik tidak dikosongkan semasa menetapkan semula pembolehubah - adakah ini pepijat dalam PHP, jenis pembolehubah dinamik ialah salah satu ciri pentingnya yang fleksibel?" Apabila kami menetapkan semula nilai jenis berbeza kepada pembolehubah, PHP akan melaraskan jenis pembolehubah secara automatik mengikut nilai baharu. Walau bagaimanapun, kadangkala kita mungkin terlupa untuk mengosongkan pembolehubah dan mendapat hasil yang tidak dijangka. Jadi, adakah keadaan ini dikira sebagai satu kesilapan? Mari kita terokai bersama-sama.

Kandungan soalan

Terdapat kebiasaan terkenal dalam Go bahawa antara muka yang memegang nilai sifar tidak sama dengan sifar. Ini kerana di bawah tudung, antara muka ialah gabungan jenis dinamik dan nilai, iaitu sifar hanya jika kedua-duanya tiada. Jadi (*MyStruct)(nil) != nil(nil)(nil) == nil. Blog ini menerangkannya dengan lebih baik.

Saya menemui sesuatu yang berkaitan dengan tingkah laku ini yang mengejutkan saya, di sini: https://goplay.tools/snippet/VF8oWt9XvO8. Kod juga disalin di bawah.

Nampaknya jika anda menetapkan semula pembolehubah yang diberikan jenis dinamik, jenis dinamik akan diingati dan dikekalkan sebagai nilai baharu. Ini nampaknya tidak dijangka kepada saya, saya fikir menugaskan semula pembolehubah harus menimpa semua keadaan lalu.

Saya menyemak spesifikasi bahasa tetapi agak kabur: https://go.dev/ref/spec#Assignability

<code>
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block ... Redeclaration does not introduce a new variable; it just assigns a new value to the original.
</code>

Tidak jelas sama ada ini bermakna nilai sahaja, atau nilai tambah penaipan dinamik.

Adakah tingkah laku ini disengajakan dalam bahasa, atau adakah ia beberapa kesan sampingan daripada masa jalan yang menggunakan semula memori untuk pengagihan semula berubah tanpa mengosongkan semua keadaan?

Kod:

package main

import (
    "fmt"
)

type CustomError struct{}

func (e *CustomError) Error() string {
    return "err"
}

// ===================

func FuncThatReturnsCustomError() *CustomError {
    return nil
}

func FuncThatReturnsCustomErrorAsErrorInterface() error {
    // although the underlying returned value is nil, the return value from this func != nil
    // https://glucn.medium.com/golang-an-interface-holding-a-nil-value-is-not-nil-bb151f472cc7
    return FuncThatReturnsCustomError()
}

// ===================

func main() {
    // Assign a non-nil value to err (value is nil, dynamic type is not)
    err := FuncThatReturnsCustomErrorAsErrorInterface()
    fmt.Printf("err == nil: %v                                    false because although the value is nil, the dynamic type is not nil (expected)\n", err == nil)

    // Call func where return type is a pointer to a struct and and returns nil
    // It is nil, as expected, this call is just to provide a comparison with the call after
    newlyDeclaredErr := FuncThatReturnsCustomError()
    fmt.Printf("newlyDeclaredErr == nil: %v                        true because func returned nil (expected)\n", newlyDeclaredErr == nil)

    // Exactly the same call as above, but reusing the original err variable instead of declaring a new one
    // Back to not nil, unexpected
    err = FuncThatReturnsCustomError()
    fmt.Printf("original err reassigned == nil: %v                false presumably because err remembered its old dynamic type even after reassignment (unexpected)\n", err == nil)

    // Reassign err again, but explicitly to nil rather than by calling a function that returns nil. This time it's nil again
    err = nil
    fmt.Printf("original err after assignment to nil == nil: %v    true, expected but not consistent with the case above\n", err == nil)
}

Penyelesaian

Bahagian "tidak dijangka" anda ialah ini:

err = FuncThatReturnsCustomError()

Di mana anda menjangkakan hasilnya ialah nilerr 是接口类型(error)的变量,FuncThatReturnsCustomError() 的返回类型为 *CustomError。这不是一个接口类型,而是一个具体类型(指向 CustomError 的指针)。由于它返回一个非接口值,因此当分配给接口类型的变量时,必须将其包装到接口值中。这是将创建非 nil nilai antara muka. Ini tiada kaitan dengan "mengingat" atau "memelihara" maklumat jenis lama.

Jika anda menggunakan fungsi dengan jenis hasil antara muka seperti:

func returnNilErr() error {
    return nil
}

dan ujinya:

err = returnNilErr()
fmt.Printf("result of returnNilErr() == nil: %v\n", err == nil)

Apa yang anda dapat (cuba di Go Playground):

result of returnNilErr() == nil: true

Sebab returnNilErr()已经有接口结果类型(error),所以它的返回值不需要打包成接口值,在赋值给err pembolehubah boleh digunakan seadanya. p>

Lihat pendua berkaitan/kemungkinan: Sembunyikan nilai sifar untuk memahami mengapa Go gagal di sini

Soalan Lazim Pergi: Mengapa nilai ralat sifar saya tidak sama dengan sifar? 一个>

Atas ialah kandungan terperinci Jenis dinamik tidak dikosongkan semasa menetapkan semula pembolehubah - adakah ini pepijat?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam