Custom Types (Aliases) and Enums? in Go learn to take advantage of them 🐹
¿Hablas español? Ver en español
One of the things I like most about go is definitely the fact of creating types based on the basic types in very popular programming languages such as typescript. You can create types this way, being very useful to encapsulate business concepts.
So I’ll give you some ideas on how to use aliases in Go.
1. Create types for business concepts.
This will help us within our code to use business concept words that a company would use when granting a requirement.
Let’s say you have a pet app that by inviting referrals generates coins that you can use within the app and that you can then claim for real money.
func GetBalance() float64 {}
func GetTotal() float64 {}
We can see that both functions return float64 but which returns the balance in dollars and which returns the balance in game currencies.
To do this we can create types for each of these units.
type USD float64
type PetCoin float64
func GetBalance() PetCoin {}
func GetTotal() USD {}
Now if I ask you, which of the functions gives us the balance of Petcoins? You could determine it thanks to what the function returns and this also helps when receiving a request.
Carlos, I need the function that obtains the Petcoins balance to be optimized
It helps us better understand the requirements by having these business concepts as part of our code.
2. Securing functions that receive many arguments of the same type.
There is something that those of us who use typed languages say a lot and it is that it is easier to avoid errors because of the types and it is true but what happens when we have a function that receives X number of arguments all of the same type.
You have to be very careful when passing the arguments or you’ll end up passing them out of order and may not catch the error until it’s too late.
Let’s see this example:
func setHeaders(IP string, Hostname string, Key string) {}
func ExampleHandler(ctx *fiber.Ctx) {
setHeaders(ctx.Hostname(),ctx.IP(),ctx.Authorization())
}
As you can see, the arguments are not in the correct order, however, this function will execute as normal and if we do not have any validation within it, it will perform its function with erroneous data.
We will now define types for each of the arguments.
type IP string
type Hostname string
type Key string
func setHeaders(ip IP, hostname Hostname, key Key) {}
func ExampleHandler(ctx *fiber.Ctx) {
setHeaders(ctx.Hostname(),ctx.IP(),ctx.Authorization()) // error
ip:= IP(ctx.IP())
hostname:= Hostname(ctx.Hostname())
key:= Key(ctx.Authorization())
setHeaders(hostname,ip,key) // error wrong type in arguments
setHeaders(ip, hostname, key) // Correct
}
Now our function will receive typed arguments of abstract concepts that do not have a data type in the language.
3. Power data types that are not required to be a structure
Many cases we will have to create data types that have only one data item within the structure but that require several methods that help us enhance the data type, however we do not want to add them to the base data type.
type IP struct {
Value string
}
func NewIP(s string) IP {}
func (i IP) Parse(s string) error {}
func (i IP) Validate() error {}
func (i IP) ToString() string {}
Definitely an IP data type is a great idea but also creating a structure is too complicated, so in order to load a common data type like a string we can use an alias like this:
package main
type IP string
func (i IP) Validate() error {
// Here validate your type
}
func (i IP) ToString() string {
return string(i)
}
func NewIP(s string) IP {
ip := IP(s)
if err:= ip.Validate(); err != nil {
panic(err.Error())
}
return ip
}
func main() {
ip:=NewIP("197.0.0.1")
println(ip.ToString())
}
This opens up many possibilities for us, such as creating functions that receive only the IP data type that do not need validation within the flow, since when it is created it is validated using a method such as the validate in the example above.
func ConnectToIP(ip IP) {}
Enums in Go?
Also thanks to the aliases in Go we can manage to simulate something very similar to an Enum in Go since it does not natively have this type of data.
type Season int
const (
Undefined Season = iota
Summer Season
Winter Season
Autumn Season
Spring Season
)
func (s Season) String() string {
switch s {
case Summer:
return "summer"
case Autumn:
return "autumn"
case Winter:
return "winter"
case Spring:
return "spring"
}
return "unknown"
}
In this way we can simulate an Enum in Go and define a set of limited options for a specific concept. Here we can see an example of how we would use our Enum in code.
func ShowSeason(s Season) {
fmt.Println(s.String())
}
func main() {
ShowSeason(Winter) // "winter"
ShowSeason(Summer) // "summer"
}
Tell me about other ways you use aliases in your Go projects so I can continue to improve this article and add new uses for this amazing Go functionality.