When I am coding, I met a error:
“runtime error: invalid memory address or nil pointer dereference”
It’s strange, encountering a “runtime error: invalid memory address or nil pointer dereference” usually means accessing memory pointed to by a nil pointer.
func (c *ThriftGeneric) Init(Conf *config.Config) error {
// Waiting for server reflection
p, err := generic.NewThriftFileProvider(Conf.IDLPath)
if err != nil {
return err
}
fmt.Println(Conf)
c.Provider = p
// The error occurs here due to nil pointer
c.Conf = Conf
g, err := generic.JSONThriftGeneric(p)
if err != nil {
return err
}
c.Generic = g
if err := c.BuildClientOptions(); err != nil {
return err
}
cli, err := genericclient.NewClient(Conf.Service, c.Generic, c.ClientOpts...)
if err != nil {
return err
}
c.Client = cli
return nil
}
Why does it throw an error here? It’s because c.Conf
is nil, even though it was working fine before. Later, I found out that I had refactored the code once:
Originally:
type ThriftGeneric struct {
Client genericclient.Client
Generic generic.Generic
Conf *config.Config
ClientOpts []client.Option
CallOptions []callopt.Option
Resp interface{}
Provider generic.DescriptorProvider
}
func NewThriftGeneric() Client {
return &ThriftGeneric{}
}
After refactoring:
type GenericClientBase struct {
Client genericclient.Client
Generic generic.Generic
Conf *config.Config
ClientOpts []client.Option
CallOptions []callopt.Option
Resp interface{}
}
type ThriftGeneric struct {
*GenericClientBase
Provider generic.DescriptorProvider
}
func NewThriftGeneric() Client {
// Upon inspection, something seemed off here
// I realized GenericClientBase wasn't initialized...
return &ThriftGeneric{}
}
After the modification, everything worked fine again. Since c.GenericClientBase.Conf
and c.Conf
are the same, directly assigning the latter without any change is sufficient.
func NewThriftGeneric() Client {
return &ThriftGeneric{
GenericClientBase: &GenericClientBase{},
}
}
But this is also correct:
type ThriftGeneric struct {
GenericClientBase
Provider generic.DescriptorProvider
}
func NewThriftGeneric() Client {
return &ThriftGeneric{}
}
This approach directly nests the GenericClientBase structure instead of using pointers. This way, when a ThriftGeneric instance is created, the initialization of GenericClientBase occurs automatically without additional steps.
My question is, how does the compiler handle the above two situations? If both are used, will the final memory layout of ThriftGeneric be the same? Which one is more recommended?