{"id":1618,"date":"2015-09-16T23:00:16","date_gmt":"2015-09-17T04:00:16","guid":{"rendered":"http:\/\/unitstep.net\/?p=1618"},"modified":"2015-09-16T23:02:13","modified_gmt":"2015-09-17T04:02:13","slug":"golang-promoted-methods-method-sets-and-embedded-types","status":"publish","type":"post","link":"https:\/\/unitstep.net\/blog\/2015\/09\/16\/golang-promoted-methods-method-sets-and-embedded-types\/","title":{"rendered":"Golang: Promoted methods, method sets and embedded types"},"content":{"rendered":"

In Go, interfaces are implemented implicitly by ensuring that the implementing type’s method set contains all the methods of the interface. That is, if a type A contains a method set that is a superset of interface I’s method set, then type A implements interface I<\/a>.<\/p>\n

This seems pretty straightforward, but can get a little convoluted when dealing with struct types that have embedded\/anonymous fields.<\/p>\n

<\/p>\n

Embedded in plain sight<\/h2>\n

In Go, a struct can have an embedded or anonymous field; the embedded type is specified just with the type name, and can either be a value type or a pointer to a such a type. For example, in the following code, the BossManager<\/code> struct embeds the types Boss<\/code> and Manager<\/code>:<\/p>\n

type Boss struct {}\r\nfunc (b *Boss) AssignWork() {\r\n\tfmt.Println(\"Boss assigned work\")\r\n}\r\n\r\ntype Manager struct {}\r\nfunc (m *Manager) PreparePowerPoint() {\r\n\tfmt.Println(\"PowerPoint prepared\")\r\n}\r\n\r\ntype BossManager struct {\r\n\tBoss\r\n\tManager\r\n}<\/code><\/pre>\n

Embedding is composition, not inheritance<\/a>, but Go also does something called “promotion”, whereby the fields or methods of embedded types become available on the outer\/embedding type. This provides a sort of automatic delegation and gives the pseudo-impression of “inheritance”, though it’s probably best not think of it that way. Here’s what the Go spec<\/a> says about promotion:<\/p>\n

A field or method f of an anonymous field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.<\/p>\n

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.<\/p><\/blockquote>\n

This means that for the above example, we can call the methods defined on the embedded types {Boss, <\/code> Manager<\/code>} as if they were defined on BossManager<\/code> itself:<\/p>\n

bm := BossManager{}\r\n\r\n\/\/ Both methods (which use pointer receivers) have been promoted to BossManager.\r\nbm.AssignWork() \/\/ \"Boss assigned work\"\r\nbm.PreparePowerPoint() \/\/ \"PowerPoint prepared\"<\/code><\/pre>\n

However, these methods are not<\/strong> included in the method set for the value type BossManager<\/code>. This is because they are defined with pointer receivers, not value receivers.<\/p>\n

This is explained in the Go specification section on struct types, but also follows from the earlier section on method sets<\/a>. I’ll summarize the salient points like so:<\/p>\n

    \n
  1. The method set of an interface type is its interface<\/li>\n
  2. The method set of any other type T consists of all methods declared with receiver type T, but does not include methods declared with a pointer receiver type *T<\/em>.<\/li>\n
  3. 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).<\/li>\n<\/ol>\n

    Point 2 is most important; if we think of the embedded types as having their methods defined on the outer type just as they were on the embedded type, the rules apply in the same way: Since {AssignWork(), PreparePowerPoint()<\/code>} are defined with a pointer receiver, the value type BossManager<\/code> doesn’t include them in its method set, even though they are still promoted methods and can be called on the type.<\/p>\n

    This also means if we define an interface like so:<\/p>\n

    \/\/ Define an interface that requires both methods.\r\ntype PromotionMaterial interface {\r\n\tAssignWork()\r\n\tPreparePowerPoint()\r\n}<\/code><\/pre>\n

    Then, the BossManager<\/code> struct, as defined above, does not<\/strong> implement this interface. However, the pointer type *BossManager<\/code> does, because its method set does<\/strong> include the promoted methods with pointer receivers. The Go specification on structs summarizes this as follows:<\/p>\n

    If S contains an anonymous 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.
    \nIf S contains an anonymous field *T, the method sets of S and *S both include promoted methods with receiver T or *T.<\/p><\/blockquote>\n

    Using embedded pointer types<\/h2>\n

    An alternative is using an embedded pointer type. This would allow the outer\/embedding type’s method set to contain the methods on the embedded type that use either a value or pointer receiver. For example:<\/p>\n

    type BossManagerUsingPointers struct {\r\n\t*Boss\r\n\t*Manager\r\n}<\/code><\/pre>\n

    This value type would implement the interface PromotionMaterial<\/code> (defined previously) and its associated pointer type would also implement the interface.<\/p>\n

    A simple example<\/h2>\n

    Since the concepts can be a bit confusing at first, I put together a small example based on the code snippets above. It’s available on the Go Playground<\/a> or below.<\/p>\n

    package main\r\n\r\nimport \"fmt\"\r\n\r\ntype Boss struct {}\r\nfunc (b *Boss) AssignWork() {\r\n\tfmt.Println(\"Boss assigned work\")\r\n}\r\n\r\ntype Manager struct {}\r\nfunc (m *Manager) PreparePowerPoint() {\r\n\tfmt.Println(\"PowerPoint prepared\")\r\n}\r\n\r\ntype BossManager struct {\r\n\tBoss\r\n\tManager\r\n}\r\ntype BossManagerUsingPointers struct {\r\n\t*Boss\r\n\t*Manager\r\n}\r\n\r\n\r\n\/\/ Define an interface that requires both methods.\r\ntype PromotionMaterial interface {\r\n\tAssignWork()\r\n\tPreparePowerPoint()\r\n}\r\n\r\nfunc promote(pm PromotionMaterial) {\r\n\tfmt.Println(\"Promoted a person with promise.\")\r\n}\r\n\r\nfunc main() {\r\n\tbm := BossManager{}\r\n\t\r\n\t\/\/ Both methods (which use pointer receivers) have been promoted to BossManager.\r\n\tbm.AssignWork() \/\/ \"Boss assigned work\"\r\n\tbm.PreparePowerPoint() \/\/ \"PowerPoint prepared\"\r\n\t\r\n\t\/\/ However, the method set of BossManager does not include either method because:\r\n\t\/\/ 1) {Boss, Manager} are embedded as value types, not pointer types.\r\n\t\/\/ 2) This makes it so that only the pointer type *BossManager includes both methods \r\n\t\/\/ in its method set, thus making it implement interface PromotionMaterial.\r\n\r\n\t\/\/ promote(bm)\t\/\/ Would fail with: cannot use bm (type BossManager) as type PromotionMaterial in argument to promote:\r\n\t\t\t\/\/ BossManager does not implement PromotionMaterial (AssignWork method has pointer receiver)\r\n\t\t\t\/\/ This would work if {Boss, Manager} were embedded as pointer types.\r\n\t\r\n\tpromote(&bm) \/\/ Works\r\n\t\r\n\t\/\/ Lets use the struct with the embedded pointer types:\r\n\tbm2 := BossManagerUsingPointers{}\r\n\tbm2.AssignWork() \/\/ \"Boss assigned work\"\r\n\tbm2.PreparePowerPoint() \/\/ \"PowerPoint prepared\"\r\n\tpromote(bm2) \/\/ Works\r\n\tpromote(&bm2) \t\/\/ Also works, since both BossManagerUsingPointers (value type) and *BossManagerUsingPointers (pointer type)\r\n\t\t\t\/\/ method sets include both methods.\r\n}<\/code><\/pre>","protected":false},"excerpt":{"rendered":"

    In Go, interfaces are implemented implicitly by ensuring that the implementing type’s method set contains all the methods of the interface. That is, if a type A contains a method set that is a superset of interface I’s method set, then type A implements interface I. This seems pretty straightforward, but can get a little […]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[494,137],"tags":[],"_links":{"self":[{"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/posts\/1618"}],"collection":[{"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/comments?post=1618"}],"version-history":[{"count":11,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/posts\/1618\/revisions"}],"predecessor-version":[{"id":1657,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/posts\/1618\/revisions\/1657"}],"wp:attachment":[{"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/media?parent=1618"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/categories?post=1618"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unitstep.net\/wp-json\/wp\/v2\/tags?post=1618"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}