I wonder when or if it should be use own type in golang. When this will be more friendly to understand my code, or when I shouldn't use own type.
Example: I want to create map
type with MAC, and name host:
in first way the simplest I can do that
var machines map[string]string{
"11:22...": "myHost",
"22:33..": "yourHost",
}
in second way
type MAC string
type HOST string
machines := map[MAC]HOST{
MAC("11:22..") : HOST("myHost"),
MAC("22:33..") : HOST("yourHost"),
}
In above exmaple I can get additional controle on my type MAC
, HOST
trought write method to validation, compare etc it is better ?
Third way
type MACHINES map[string]string
m := MACHINES{}
m = map[string]string{
"11:22.." : "myHost",
"22:33" : "yourHost",
}
above example for me is worst to understand less intuitive to some else. I think that above example should be also filled about HOST, and MAC because type MACHINE nothing to say developer how this should be implement so I would like
type MACHINES map[MAC]HOST
However, please about comment to better understand about usage own type in golang.
Creating type alias is only useful when you require to add extra methods (such as validation functions) or when you want to document the desired use of some value (for example, the net.IP type).
Type alias could help you to prevent API misunderstanding, but won't if you're using constant values. For example, this code is valid:
type Host string
type Mac string
hosts := map[Mac]Host{"ad:cb..": "localhost"}
For further information about how constants work in Go, you can check the Rob Pike's blog post
One of the most important features of Go is interfaces. In Go by defining the method/s of an Interface you implement the interface, and the only way to implement an interface is by adding a Method to your type. For instance, say you want to implement the Stringer
interface from the fmt
package.
In your example type MACHINES map[string]string
you would add a method called String
to your type.
func (m MACHINES) String() string {
s := "MACHINES
"
for k, v := range m {
s += fmt.Sprintf("%v: %v
", k, v)
}
return s
}
Any other function that accepts the Stringer
interfaces can now also accept your MACHINES type since you implemented the Stringer
interface.
For example the fmt.Printf
checks if the passed in type implements the Stringer
interface
m := MACHINES{"foo":"bar", "baz": "bust"}
fmt.Printf("%s", m)
Will invoke MACHINES String method
Example from the playground
Without commenting on your specific example, there are a few reasons you'd generally want to use a new type:
net.Conn
, you might create a type Conn net.Conn
and return that instead just for the sake of grouping the functions under the Conn type header in godoc or to provide general documentation for the net.Conn returned by the methods).You do want to use your own types in my opinion.
As an example, lists of function arguments that are all "string" or all "int" are confusing and very easy to get wrong.
And a comment on your type names. "MAC" is an acronym for Media Access Control so it should stay as all caps. But "HOST" should be "Host" since it is just a word. I don't recall where it is exactly, but there's a recommended form for Go names which is CamelCase with all-capital abbreviations, like "GetIPv4AddressFromDNS"
I think another reason to use your own types, that hasn't been mentioned here, is if you are not sure exactly what the right type for something is e.g. uint8 / uint16 and you want to easily change it later all in one place.
This will however make conversions necessary whenever you want to use the built-in types' methods. Or you will need to define them on your own type.