Go allows you to define a struct that has fields but with no variable names. These fields are called anonymous fields. Let’s do a few examples to find out what they are and how they will be useful.
In the below example, we have defined a Kitchen struct, which only contains the number of plates as a field. We define another field called House, which contains an instance of Kitchen as a field - but it is an anonymous field, because we have not given a variable name for it.
The first important thing to note is that since we have defined Kitchen to be an anonymous field, it allows us to access its members as if they were members of the encompassing class. In comparison, if you were using a language like Java, you would have to do:
The second important thing to note is that the composed field is still available to be accessed, but by its type name. So in this case the anonymous field for Kitchen has to be accessed as
And the third important thing to note is how we had to use the type name when initializing the values as we did:
So there is a rule on field resolution when the same field occurs at different levels of composition. But there is no rule when the fields are at the same level of composition - which means that when it occurs you need to resolve it yourself.
In the code below, both the
To resolve this, you will have to refer to the required fields explicitly via the type name of the anonymous field. In the corrected example below, we’ve summed up the number of lamps in the kitchen and the bedroom by referring to the number of lamps in it via its type name.
In the below example, we have defined a Kitchen struct, which only contains the number of plates as a field. We define another field called House, which contains an instance of Kitchen as a field - but it is an anonymous field, because we have not given a variable name for it.
Full code
package main import "fmt" type Kitchen struct { numOfPlates int } type House struct { Kitchen //anonymous field numOfRooms int } func main() { h := House{Kitchen{10}, 3} //to initialize you have to use composed type name. fmt.Println("House h has this many rooms:", h.numOfRooms) //numOfRooms is a field of House fmt.Println("House h has this many plates:", h.numOfPlates) //numOfPlates is a field of anonymous field Kitchen, so it can be referred to like a field of House fmt.Println("The Kitchen contents of this house are:", h.Kitchen) //we can refer to the embedded struct in its entirety by referring to the name of the struct type }
House h has this many rooms: 3
House h has this many plates: 10
The Kitchen contents of this house are: {10}
House h has this many plates: 10
The Kitchen contents of this house are: {10}
The first important thing to note is that since we have defined Kitchen to be an anonymous field, it allows us to access its members as if they were members of the encompassing class. In comparison, if you were using a language like Java, you would have to do:
Partial Java code
public class Kitchen { public int numOfPlates; } public class House { public Kitchen kitchen; } //and in main public static void main(String[] args) { House h = new House(); h.kitchen.numOfPlates = 10; //referred as a sub field item. }
The second important thing to note is that the composed field is still available to be accessed, but by its type name. So in this case the anonymous field for Kitchen has to be accessed as
h.Kitchen
. If you want to print the number of plates in the kitchen, then do this: fmt.Println(h.Kitchen.numOfPlates)
.And the third important thing to note is how we had to use the type name when initializing the values as we did:
h := House{Kitchen{10}, 3}
. It is necessary here that you state the type name and provide its values within the corresponding curly braces. So h := House{{10}, 3}
and h := House{10, 3}
will cause compilation errors.Anonymous fields - when naming conflicts arise
What happens when more than one of the composed structs or the composing struct has the same field name. If there is a field in an outer struct with the same name as a field in an inner anonymous struct, then the outer one is accessible by default. In the below example, both theKitchen
and the House
has a numOfLamps
field, but since House
is the outer struct, its numOfLamps
hides Kitchen
’s. If you still require to access the Kitchen
’s numOfLamps
, that is possible by referring to it via the type name: h.Kitchen.numOfLamps
.Full code
package main import "fmt" type Kitchen struct { numOfLamps int } type House struct { Kitchen numOfLamps int } func main() { h := House{Kitchen{2}, 10} //kitchen has 2 lamps, and the House has a total of 10 lamps fmt.Println("House h has this many lamps:", h.numOfLamps) //this is ok - the outer House's numOfLamps hides the other one. Output is 10. fmt.Println("The Kitchen in house h has this many lamps:", h.Kitchen.numOfLamps) //we can still reach the number of lamps in the kitchen by using the type name h.Kitchen }
House h has this many lamps: 10
The Kitchen in house h has this many lamps: 2
The Kitchen in house h has this many lamps: 2
So there is a rule on field resolution when the same field occurs at different levels of composition. But there is no rule when the fields are at the same level of composition - which means that when it occurs you need to resolve it yourself.
In the code below, both the
Kitchen
and the Bedroom
have a field numOfLamps
, and both are available as an anonymous field within House
. Now if we referred to House.numOfLamps
, the Go compiler cannot resolve whether you are referring to the numOfLamps
within Kitchen
or that within Bedroom
and it throws an error.Full file: structs2.go
package main import "fmt" type Kitchen struct { numOfLamps int } type Bedroom struct { numOfLamps int } type House struct { Kitchen Bedroom } func main() { h := House{Kitchen{2}, Bedroom{3}} //kitchen has 2 lamps, Bedroom has 3 lamps fmt.Println("Ambiguous number of lamps:", h.numOfLamps) //this is an error due to ambiguousness - is it Kitchen.numOfLamps or Bedroom.numOfLamps }
Compiler error
8g -o _go_.8 structs2.go
structs2.go:20: ambiguous DOT reference House.numOfLamps
make: *** [_go_.8] Error 1
structs2.go:20: ambiguous DOT reference House.numOfLamps
make: *** [_go_.8] Error 1
To resolve this, you will have to refer to the required fields explicitly via the type name of the anonymous field. In the corrected example below, we’ve summed up the number of lamps in the kitchen and the bedroom by referring to the number of lamps in it via its type name.
package main import "fmt" type Kitchen struct { numOfLamps int } type Bedroom struct { numOfLamps int } type House struct { Kitchen Bedroom } func main() { h := House{Kitchen{2}, Bedroom{3}} fmt.Println("House h has this many lamps:", h.Kitchen.numOfLamps + h.Bedroom.numOfLamps) //refer to fields via type name }
House h has this many lamps: 5
Articles are very good, although it's more like a book)
ReplyDeleteThank you.
'h := House{Kitchen{2}, Bedroom{3}} //kitchen has 2 lamps, Bedroom has 3 lamps, and the House has a total of 10 lamps'
ReplyDeleteThere's no integer being set to 10 there or anywhere in that example.
Thank you, have updated the comment in the code.
ReplyDeleteBut what if we need to define two or more Kitchen for House?
ReplyDeletein the java code....
ReplyDeletepublic class Kitchen {
int numOfPlates;
}
it's not public... it's default( ) accessor: go lower case field/struct/funcion can be seen within package level.
In the Java code, I've written "public int numOfPlates". I think you dropped the 'public' keyword in your comment.
DeleteHow would you define a struct with multiple anonymous fields of the same type i.e. array of anonymous fields. E.g How to define a House with multiple Bedrooms (in reference to your last example).
ReplyDeletetype Bedroom struct {
DeletenumOfRooms int
}
type Bedrooms []Bedroom
type House struct {
Bedrooms
}
func main() {
bedrooms := Bedrooms{Bedroom{1}, Bedroom{2}, Bedroom{3}}
house := House{bedrooms}
fmt.Println(len(house.Bedrooms))
}
Great article, thanks!
ReplyDeleteGreat, thanks!
ReplyDeleteI like this tut, Thanks!
ReplyDelete