[Go] use custom type to improve your code
Tags: go
Published: 2020-01-28 18:01:34

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:
```go
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

```go
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.

```go
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
```golang
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
```golang
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.

[json.Number](https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L188)

```go
// 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.