In today's post, I will introduce some techniques and methods to set default values ​​in Go structs. We already have some articles about structs, marshal, unmarshal, and constants in structs,... you can refer to these posts to deeply understand how to work with structs in Golang.
Method 1: Using a separate constructor function for the structs
The idea is quite simple. We will create a function that returns a struct with some default value.
package main
import "fmt"
type Student struct {
Name string
School string
Age int
}
// Set default value for a instance
func SetDefault(name string) Student {
st := Student{}
st.Name = name
st.School = "Go LinuxCloud"
st.Age = 25
return st
}
func main() {
defaultStudent := SetDefault("Harry Potter")
fmt.Printf("The default student: %+v\n", defaultStudent)
student := new(Student)
fmt.Printf("The empty student: %+v\n", student)
}
Output:
The default student: {Name:Harry Potter School:Go LinuxCloud Age:25}
The empty student: &{Name: School: Age:0}
Method 2: Using an Init() function
The idea for this method is to create an Init()
function for that struct. The code below shows how we can do that in Golang:
package main
import "fmt"
type Student struct {
Name string
School string
Age int
}
// Init is the main function that initiate the structure, and return it
func (st Student) Init() Student {
st.Name = "Default Value"
st.School = "Go LinuxCloud"
st.Age = 25
return st
}
func main() {
defaultStudent := new(Student).Init()
fmt.Printf("The default student: %+v\n", defaultStudent)
}
Output:
The default student: {Name:Default Value School:Go LinuxCloud Age:25}
Tags offer a method of doing this that enables multiple defaults. Suppose you have the struct below, which has the default tags default0 and default1. We will try to create 2 default structs in the example below:
package main
import (
"fmt"
"reflect"
"strconv"
)
type Student struct {
Name string `default0:"Harry Potter" default1:"Custom Name"`
School string `default0:"GoLinuxCloud" default1:"Google"`
Age int `default0:"3" default1:"42"`
}
func main() {
std1 := &Student{}
Set(std1, "default0")
fmt.Printf("The first default value: %+v\n", std1)
std2 := &Student{}
Set(std2, "default1")
fmt.Printf("The second default value: %+v\n", std2)
}
func setField(field reflect.Value, defaultVal string) error {
if !field.CanSet() {
return fmt.Errorf("Can't set value\n")
}
switch field.Kind() {
case reflect.Int:
if val, err := strconv.ParseInt(defaultVal, 10, 64); err == nil {
field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
}
case reflect.String:
field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
}
return nil
}
func Set(ptr interface{}, tag string) error {
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
return fmt.Errorf("Not a pointer")
}
v := reflect.ValueOf(ptr).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
if defaultVal := t.Field(i).Tag.Get(tag); defaultVal != "-" {
if err := setField(v.Field(i), defaultVal); err != nil {
return err
}
}
}
return nil
}
Output:
The first default value: &{Name:Harry Potter School:GoLinuxCloud Age:3}
The second default value: &{Name:Custom Name School:Google Age:42}
Explanation
We will create 2 helper functions: Set() and SetField(). These two functions will iterate all the fields in the struct and set the value in the tag to each field.
Summary
A constructor function can be used to give default values to a struct. Instead of explicitly building a structure, we may use a constructor to give all or some of its members custom default values.
References
https://pkg.go.dev/encoding/json
How to set default values in Go structs - Stack Overflow