Can someone explain me why this code prints 1 and not 2?
package main
import (
"fmt"
)
type S1 struct{
f1 string
}
type S2 struct{
S1
f2 string
}
func (s *S1) Say(){
fmt.Println("1")
}
func (s *S2) Say(){
fmt.Println("2")
}
type S3 S2
func main() {
var s3 S3
s3.Say()
}
(Runnable at: https://play.golang.org/p/_cjNxBKgSf)
See this answer.
Specifically, from the Go spec we have Method Sets:
Method sets
A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.
Then Struct typess:
Struct types
A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-blank field names must be unique.
Then this:
A field declared with a type but no explicit field name is called an embedded field.
Finally, this:
A field or method f of an embedded field in a struct x is called promoted if
x.f
is a legal selector that denotes that field or methodf
.Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.
Given a struct type S and a type named T, promoted methods are included in the method set of the struct as follows:
If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T. If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.
How does all that combine?
You have
type S2 struct{
S1
f2 string
}
which makes S1 an embedded field, and makes S1.Say visible.
Then you have:
type S3 S2
Which makes S3 have the same memory layout and fields as S2, but does not create a type equivalence. This is not saying that S3 "is a" S2, but rather that S3 is not the same as S2, but they do have the same layout.
That layout includes embedded fields, which happens to bring S1.Say into the equation.
Put another way, type S2 has an underlying type of:
struct { S1; f2 string }
and a method called Say.
Type S3 has an identical underlying type of:
struct { S1; f2 string }
But S3 and S2 are not the same, and so S3 does not "inherit" any methods from S2. Instead, S3 inherits only the fields/methods from its underlying type, which are f2, and S1.* (including "Say").
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With