I will not discuss struct type here, what I would like to focus is custom type for basic type. What is a custom type for basic type? You could think it likes a alias name of basic type, for example:
type Username string
type ErrorCode int
Readability
Why we should use custom type instead of basic type?
One of my opinion is readability, like the above example, if you only give an int for error code, it's not so easy to understand.
compare the following 2 functions, assign specific type to arguments make the function easier to understand
func Print(s string) {
fmt.Println(s)
}
func Print(u Username) {
fmt.Println(u)
}
Cohesion
Another benefit is, it can have superpower to use method like struct.
Then the code has high cohesion, and because of explicit/specific type, we could have more intellisense/auto-complete support from editor/IDE.
func Print(s string) {
fmt.Println(s)
}
func Print(u Username) {
fmt.Println(u)
}
func (u Username) Print() {
fmt.Println(u)
}
Let's write another example:
scenario: we would like to extract origin, productId, prodcution year, batch number from a product serial number,
first version: only use basic type
func main() {
p := "XX-0010-2020-0015"
fmt.Println(GetOrigin(p))
fmt.Println(GetProductId(p))
fmt.Println(GetProductionYear(p))
fmt.Println(GetBatchNumber(p))
}
const (
origin = iota
productId
productionYear
batchNumber
)
func GetOrigin(s string) string {
return getInformation(s, origin)
}
func GetProductId(s string) string {
return getInformation(s, productId)
}
func GetProductionYear(s string) string {
return getInformation(s, productionYear)
}
func GetBatchNumber(s string) string {
return getInformation(s, batchNumber)
}
func getInformation(s string, field int) string {
return strings.Split(s, "-")[field]
}
second version: use custom type of string
func main() {
p:= ProductSerialNumber("XX-0010-2020-0015")
fmt.Println(p.GetOrigin())
fmt.Println(p.GetProductId())
fmt.Println(p.GetProductionYear())
fmt.Println(p.GetBatchNumber())
}
type ProductSerialNumber string
const (
origin = iota
productId
productionYear
batchNumber
)
func (p ProductSerialNumber) GetOrigin() string {
return p.getInformation(origin)
}
func (p ProductSerialNumber) GetProductId() string {
return p.getInformation(productId)
}
func (p ProductSerialNumber) GetProductionYear() string {
return p.getInformation(productionYear)
}
func (p ProductSerialNumber) GetBatchNumber() string {
return p.getInformation(batchNumber)
}
func (p ProductSerialNumber) getInformation(field int) string {
return strings.Split(string(p), "-")[field]
}
Which one you prefer, if you may change or reorganize code 1 year later.
Example in standard library
Let's look one example in encoding/json.
json.Number is essentially a string which decode a number from json into string format, but with custom type, we can easily convert to string/float/int
as we need.
// A Number represents a JSON number literal.
type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
Conclusion
Yes, using custom type may need to write more code(but not so much), but it provides good readability and cohesion. From my point of view, it's worthwhile.
Top comments (0)