ホームページ >バックエンド開発 >Golang >Goで型比較を分析する

Goで型比較を分析する

藏色散人
藏色散人転載
2021-06-21 16:09:533381ブラウズ

golang の次のチュートリアル コラムでは、go での型比較の分析について紹介します。

概要

最近のインタビューで、インタビュアーは go In 間の型について質問しました。比較してみると、答えはあまりよくありません。根本的に言えば、基礎が十分にしっかりしていないのです。インターネットでたくさんの情報を読んで、自分で簡単にまとめてみました(笑)。

go の型

最初に、go に含まれる最も基本的な集中型を見てみましょう

    基本的な型: go の最も基本的な型には、整数 (
  • int、uint、int8、uint8、int16、uint16、int32、uint32、int64、uint64、byte、rune など)、浮動小数点型 (float32、float64)、文字列(string は []rune 配列でもあります)、あまり一般的ではありませんが複数型 (complex64/complex128) です。
  • 複合タイプ: 主に
  • structuralarray が含まれます。
  • 参照タイプ:
  • スライス、マップ、チャネル、ポインター
  • インターフェイス タイプ:
  • エラー、io.Reader など。
Go は厳密に型指定された言語であるため、PHP などの高級言語による型変換の実行を自動的にはサポートしないため、比較する際には

== を使用する必要があります。たとえ底部のタイプが同じであっても、両側のタイプは一貫している必要があります。以下のコードを見てください

package main
import "fmt"
type A struct {
    Id int
}
type B struct {
    Id int
}
func main() {
    var a int
    var b int16
    // 编译报错:invalid operation a == b (mismatched types int and int16)
    fmt.Println(a == b)
    aStruct := A{Id:5}
    bStruct := B{Id:5}
    // 编译报错:invalid operation: aStruct == bStruct (mismatched types A and B)
    fmt.Println(aStruct == bStruct)
}
したがって、go は暗黙的な変換を行いません。基になる型が一貫していても、それらを比較することはできません。

次に、異なるタイプから比較できるかどうかを分析します。

基本型

go の基本型は比較的単純です。型が同じであれば比較できます。例:

package main
import "fmt"
func main() {
    var a int = 0
    var b int = 1
    // 输出false
    fmt.Println(a == b)
}
But inまた、浮動小数点型の比較は現実と同じではないことに注意してください。たとえば、計算で 0.1 0.2 の結果は 0.3 ではなく、0.30000000000000004 になります。 draveness を見てください( https://github.com/draveness) ボスによるこの記事 https://draveness.me/whys-the...

Compound type

Array

Go アレイとスライスの違いは、インタビューでよく質問されます。 Go の配列の長さは最初に決定する必要があります。つまり、長さを拡張することはできません。値のコピーなので、パラメータを関数に渡して変更しても、外部の値は変わりません。スライスはその逆です。したがって、配列が比較できるかどうかは、次の例を見てください。

package main
import "fmt"

func main() {
    var a float64=0.1
    var b float64=0.2
    // 0.30000000000000004
    fmt.Println(a+b)
}

同じ長さの配列は比較できますが、異なる長さの配列は比較できないことがわかります

。理由は何ですか?これは、配列型では配列の長さも型の一部であるためで、長さが異なる配列は異なる型とみなされ、比較することができません。

構造同じ

構造

は同じです。

Struct

の比較も内部型から始まり、各型の値は等しい場合のみ等しくなります。以下の例: <pre class="brush:php;toolbar:false">package main import &quot;fmt&quot; func main() {     a := [2]int{1, 2}     b := [2]int{1, 2}     c := [2]int{1, 3}     d := [3]int{1, 2, 4}     fmt.Println(a == b) // true     fmt.Println(a == c) // false     fmt.Println(a == d) // invalid operation: a == d (mismatched types [2]int and [3]int) }</pre> したがって、Struct 構造体を比較できることがわかります。別の例を見てみましょう。

package main
import "fmt"
type A struct {
    id int
    name string
}
func main() {
    a := A{id:5,name:"123"}
    b := A{id:5,name:"123"}
    c := A{id:5,name:"1234"}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
}

はどのようにして再び比類のないものになったのでしょうか?これは、以下の参照タイプによって異なります。 参照型

上記の例の構造はスライスと比較できません。goでは、

Slice

Map

が比較できない型として定義されています。 。 を見てみましょう。Slice が同等である場合、同じスライスを定義するために何が使用されますか?アドレスを使用する場合、2 つのアドレスが指す

Slice

が同じである場合はどうなるでしょうか?これは明らかに不適切です。配列と同じである場合、スライスを展開すると等しくなくなります。したがって、長さと容量を比較することは困難です。この問題は言語レベルで解決できますが、golang チームは、努力する価値はないと考えています。したがって、Slice は比較不可能とみなされます。 同じ Map も比較不可能な型として定義されています。参照型は比較できないのでしょうか? いいえ、例を見てください:

package main
import "fmt"
type A struct {
    id int
    name string
    son []int
}
func main() {
    a := A{id:5,name:"123",son:[]int{1,2,3}}
    b := A{id:5,name:"123",son:[]int{1,2,3}}
    fmt.Println(a == b) // invalid operation: a == b (struct containing []int cannot be compared)
}

参照型変数には、特定の変数のメモリ アドレスが格納されます。したがって、参照型変数を比較すると、2 つの参照型が同じ変数を格納するかどうかが決まります。 同じ変数の場合、メモリ アドレスは同じである必要があり、参照型変数は等しいため、「==」を使用して true を判断します。

Ifは同じ変数ではなく、メモリです。 アドレスは明らかに異なり、「==」の結果は false です。
  • インターフェイスの種類
  • Go 言語では、インターフェイスの種類を次の 2 つのカテゴリに分類します。インターフェイス タイプに一連のメソッドが含まれているかどうか:
  • 使用 runtime.iface结构体表示包含方法的接口
  • 使用 runtime.eface结构体表示不包含任何方法的 interface{} 类型
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}

所以我们可以得知,一个接口值是由两个部分组成的,即该接口对应的类型和接口对应具体的值。接口值的比较涉及这两部分的比较,只有当类型和值都相等(动态值使用==比较),两个接口值才是相等的。看个例子:

var a interface{} = 0
var b interface{} = 2
var c interface{} = 0
var d interface{} = 0.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false

ac类型相同(都是int),值也相同(都是0,基本类型比较),故两者相等。 ab类型相同,值不等,故两者不等。 ad类型不同,aintdfloat64,故两者不等。

type A struct {
    a int
    b string
}

var a interface{} = A { a: 1, b: "test" }
var b interface{} = A { a: 1, b: "test" }
var c interface{} = A { a: 2, b: "test" }

fmt.Println(a == b) // true
fmt.Println(a == c) // false

var d interface{} = &A { a: 1, b: "test" }
var e interface{} = &A { a: 1, b: "test" }
fmt.Println(d == e) // false

ab类型相同(都是A),值也相同(结构体A),故两者相等。 ac类型相同,值不同,故两者不等。 de类型相同(都是*A),值使用指针(引用)类型的比较,由于不是指向同一个地址,故不等。

不过需要注意的是,如果接口中类型是切片或者Map不可比较的类型,那么会直接报错的。看个例子:

var a interface{} = []int{1, 2}
var b interface{} = []int{1, 2}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)

ab的类型是切片类型,而切片类型不可比较,所以a == bpanic

接口值的比较不要求接口类型(注意不是动态类型)完全相同,只要一个接口可以转化为另一个就可以比较。例如:

var f *os.File

var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true

var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)

r的类型为io.Reader接口,rc的类型为io.ReadCloser接口。查看源码,io.ReadCloser的定义如下:

type ReadCloser interface {
    Reader
    Closer
}

io.ReadCloser可转化为io.Reader,故两者可比较。

io.Writer不可转化为io.Reader,编译报错。

总结

  • 可比较:int、ifloat、string、bool、complex、pointe、channel、interface、array
  • 不可比较:slice、map、function
  • 复合类型中如果带有不可比较的类型,那么该类型也是不可比较的。可以理解不可比较类型具有传递性。

以上がGoで型比較を分析するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。