Search code examples
gopointersreferencego-gorm

Go: I get a struct field value without deferencing the pointer variable of the struct type, why?


// config/config.go

package config

import (
        "github.com/spf13/viper"
)

type Config struct {
        Database DatabaseConfig `mapstructure:"database"`
        Server   ServerConfig   `mapstructure:"server"`
        Redis    RedisConfig    `mapstructure:"redis"`
        Jwt      JwtConfig      `mapstructure:"jwt"`
        Smtp     SmtpConfig     `mapstructure:"smtp"`
}

func New() (config *Config) {
        viper.AddConfigPath(".")
        viper.SetConfigName("config")

        if err := viper.ReadInConfig(); err != nil {
                panic(err)
        }

        if err := viper.Unmarshal(&config); err != nil {
                panic(err)
        }

        return
}
// db/connection.go
package db

import (
        "fmt"

        "my.project/config"
        "gorm.io/driver/mysql"
        "gorm.io/gorm"
)

func New(config config.DatabaseConfig) *gorm.DB {
        dsn := fmt.Sprintf(
                "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
                config.Username, config.Password, config.Host, config.Port, config.Database,
        )

        db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

        if err != nil {
                fmt.Println("Failed to connect to database")
                panic(err)
        }

        RunMigrations(db)

        return db
}
// main.go
package main

import (
        "fmt"

        "my.project/config"
)

func main() {
        config := config.New()
        fmt.Println(config.Database)
        db := db.New(config.Database)

        if db == nil {
                panic("Failed to connect to database")
        }
}

In the above snippets, config is a pointer to a Config type variable (apparently from the return type of config.New()), but config.Database seems to be a value (not a reference).
I assumed config.Database would be a reference either but it's not (I tried to print out them, config was definitely a reference and config.Database was a value).
Can anyone help me to understand why? Thank you in advance!


Solution

  • It turned out that this was originated from one of the language design decisions. In fact, if you use (*config).Database, it is absolutely valid. However, the makers of Go deemed this notation cumbersome, so the language permits us to write config.Database, without an explicit dereference. These pointers to structs even have their own name struct pointers and they are automatically dereferenced. Here's another superb explanation for "auto-deferencing": When do Go's pointers dereference themselves