1

go koan をしようとして、interface(struct) 構文を理解するのに行き詰まりました。正確には何をしますか? 次の楽しいプログラムを思いつきました。これにより、インターフェイスのキャストがどのように機能するかについてさらに混乱しました。

package main

import "fmt"

type foo interface{  fn() }

type t struct { }
type q struct { }

func (_i t ) fn() { fmt.Print("t","\n") }
func (_i q ) fn() { fmt.Print("q","\n")}

func main() {
    _j :=  t{}
    _q :=  q{}

    // This is alright ..
    fmt.Print( _j.fn,"\n")           //0x4015e0     
    fmt.Print( _q.fn,"\n")       //0x401610     
    _j.fn()              //t           
    _q.fn()              //q           
    // both pointers same .. why ?
    fmt.Print( foo(_j).fn,"\n")  //0x401640     
    fmt.Print( foo(_q).fn,"\n")  //0x401640     
    // but correct fns called .. how ?
    foo(_j).fn()             //t           
    foo(_q).fn()             //q           

    // same thing again ...
    _fj := foo(_j).fn         
    _fq := foo(_q).fn         
    // both pointers same .. as above
    fmt.Print( _fj,"\n")         //0x401640    
    fmt.Print( _fq,"\n")         //0x401640    
    // correct fns called .. HOW !
    _fj()                //t                          
    _fq()                //q           
}

ポインターは、私が自分のマシン、YMMV を取得しているものです。私の質問は.. interface(struct) は正確に何を返しますか? そして、 interface(struct). func はどのように元の構造体を見つけますか...ここでサンク/スタブマジックが行われていますか?

4

1 に答える 1

3

ここから: http://research.swtch.com/interfaces

ここに画像の説明を入力

正確には何をinterface(struct)返しますか?

具体的な構造体値をラップして、新しいインターフェイス値 (図の一番上に表示されているような値) を作成します。

元の構造体をどのようにinterface(struct).func見つけますか?

グラフィックのデータフィールドを参照してください。ほとんどの場合、これは既存の値へのポインターになります。ただし、適合する場合は、値自体が含まれることもあります。

itableには、関数テーブル ( fun[0]がある場所) が表示されます。

あなたのマシン0x401640では、 へのそれぞれのポインターのアドレスであると仮定します。fnこれは、 のテーブルにありfooます。これは、GC コンパイラ スイートに取り組んでいる人によって最もよく検証されています。

あなたが発見した動作は厳密にはそうであると定義されていないことに注意してください。コンパイラ ビルダーは、必要に応じて、言語のセマンティクスが保持されている限り、Go インターフェースを実装するために他のアプローチを取ることができます。


コメントの質問に答えるために編集します。

package main

import "fmt"

type foo interface {
    fn()
}

type t struct{}
type q struct{}

func (_i t) fn() { fmt.Print("t", "\n") }
func (_i q) fn() { fmt.Print("q", "\n") }

func main() {
    _j := t{}
    _j1 := t{}

    fmt.Println(foo(_j) == foo(_j))  // true
    fmt.Println(foo(_j) == foo(_j1)) // true
}

図には、次の 3 つのブロックが表示されます。

  • Binaryというラベルの付いた左側のものは、構造体インスタンス_j_j1.

  • 上部中央のものはインターフェース値で、これは具体的な値をラップ (読み取り: 指し示す) します。

  • 右下のブロックは、Binary原資産のインターフェイス定義です。これは、ジャンプ テーブル/コール転送テーブルがある場所です ( itable )。

_j_j1は具象型の 2 つのインスタンスですt。したがって、メモリのどこかに左下のブロックが 2 つあります。

_jここで、 type のおよび_j1in インターフェイス値の両方をラップすることにしますfoo。これで、メモリのどこかに上部中央のブロックが 2 つあり、 と を指してい_jます_j1

インターフェイス値がその基になる型が何であるか、およびそれらの型のメソッドがどこにあるかを記憶するために、メモリ内の右下のブロックの単一_jのインスタンスを保持し_j1ます。

そのブロックには、インターフェイス値で行われたメソッド呼び出しを具体的な基になる型の実装に転送するためのジャンプ テーブルがあります。そのため、どちらも同じです。

Java や C++ (Python については不明) とは異なり、すべての Go メソッドは静的であり、ドット呼び出し表記は構文糖衣にすぎないことに注意してください。したがって、異なるメソッドは_jあり_j1ませんfn。メソッドが呼び出されるレシーバーである別の暗黙的な最初のパラメーターで呼び出されるのとまったく同じメソッドです。

于 2013-10-16T06:52:09.413 に答える