如何在结构体内部使用结构体?无效的内存地址或空指针引用。

huangapple go评论81阅读模式
英文:

How to use the struct inside a struct? invalid memory address or nil pointer dereference

问题

我在使用Queue时遇到了一个恐慌。这是我的结构体:

type RabbitMQ struct {
	Connection    *amqp.Connection
	Channel       *amqp.Channel
	Queue         amqp.Queue // 用于消费者
	done          chan os.Signal
	notifyClose   chan *amqp.Error
	notifyConfirm chan *amqp.Confirmation
	IsConnected   bool
	alive         bool
	Done          chan os.Signal
	Err           chan error
	Wg            *sync.WaitGroup
}

这是原始库中的Queue结构体:

type Queue struct {
	Name      string // 服务器确认或生成的名称
	Messages  int    // 未等待确认的消息数
	Consumers int    // 接收投递的消费者数量
}

在这个函数中,我尝试使用r.Queue

func (r *RabbitMQ) InitQueueForConsumer() {
	log.Println("inside initqueue")
	var err error
	r.Queue, err = r.Channel.QueueDeclare(
		"",    // 名称
		false, // 持久化
		false, // 未使用时删除
		true,  // 独占
		false, // 不等待
		nil,   // 参数
	)
	if err != nil {
		log.Printf("failed to declare a queue")
	}

	log.Println("inside initqueue")

	err = r.Channel.QueueBind(
		r.Queue.Name,
		"",
		"logs",
		false,
		nil,
	)
	if err != nil {
		log.Printf("failed to bind a queue")
	}
	log.Println("inside initqueue")
}

func (r *RabbitMQ) Consume() {
	msgs, err := r.Channel.Consume(
		r.Queue.Name,
		"",
		true,
		false,
		false,
		false,
		nil,
	)
	if err != nil {
		log.Printf("failed to register a consumer")
	}

	forever := make(chan bool)

	go func() {
		for {
			select {
			case msg, ok := <-msgs:
				if !ok {
					log.Printf("something wrong")
					return
				}
				log.Printf("Received message: [%v]\n", msg)
			}
		}
	}()

	log.Printf("Waiting for logs")
	<-forever

}

我像这样调用这个函数:

rmq := shared.RabbitMQ{}

go rmq.New()
for {
	go rmq.InitQueueForConsumer()
}

当我运行时,我得到了以下的恐慌:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x54 pc=0x11c27c6]

goroutine 22 [running]:
github.com/streadway/amqp.(*Channel).send(0x0, 0x1283c78, 0xc0000b0000, 0xc000071e28, 0x100e86a)
	/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:157 +0x26
github.com/streadway/amqp.(*Channel).call(0x0, 0x1283c78, 0xc0000b0000, 0xc000071f40, 0x1, 0x1, 0xc000100050, 0x2)
	/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:171 +0x5d
github.com/streadway/amqp.(*Channel).QueueDeclare(0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:767 +0x150
github.com/bariis/rd/shared.(*RabbitMQ).InitQueueForConsumer(0xc000152000)
	/Users/barisertas/workspace/golang-exercises/rd/shared/rabbitmq.go:151 +0x90
created by main.main
	/Users/barisertas/workspace/golang-exercises/rd/receiver1/main.go:67 +0x76
exit status 2

这是原始库中queueDeclare函数的返回值:

func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
	if err := args.Validate(); err != nil {
		return Queue{}, err
	}

	req := &queueDeclare{
		Queue:      name,
		Passive:    false,
		Durable:    durable,
		AutoDelete: autoDelete,
		Exclusive:  exclusive,
		NoWait:     noWait,
		Arguments:  args,
	}
	res := &queueDeclareOk{}

	if err := ch.call(req, res); err != nil {
		return Queue{}, err
	}

	if req.wait() {
		return Queue{
			Name:      res.Queue,
			Messages:  int(res.MessageCount),
			Consumers: int(res.ConsumerCount),
		}, nil
	}

	return Queue{Name: name}, nil
}

我尝试了各种指针、地址等的组合,我尝试在New函数中初始化它,因为它在QueueDeclare函数之前被调用,我以为它不会引发恐慌,但没有解决方案。我该如何正确地做这个?

英文:

I am getting an panic when I want to use Queue. This is my struct:

type RabbitMQ struct {
Connection    *amqp.Connection
Channel       *amqp.Channel
Queue         amqp.Queue // for consumer
done          chan os.Signal
notifyClose   chan *amqp.Error
notifyConfirm chan *amqp.Confirmation
IsConnected   bool
alive         bool
Done          chan os.Signal
Err           chan error
Wg            *sync.WaitGroup
}

This is the original Queue struct inside original library.

type Queue struct {
Name      string // server confirmed or generated name
Messages  int    // count of messages not awaiting acknowledgment
Consumers int    // number of consumers receiving deliveries
}

Here in this function I am trying to use r.Queue by:

func (r *RabbitMQ) InitQueueForConsumer() {
log.Println(&quot;inside initqueue&quot;)
var err error
r.Queue, err = r.Channel.QueueDeclare(
&quot;&quot;,    // name
false, // durable
false, // delete when unused
true,  // exclusive
false, // no-wait
nil,   // arguments
)
if err != nil {
log.Printf(&quot;failed to declare a queue&quot;)
}
log.Println(&quot;inside initqueue&quot;)
err = r.Channel.QueueBind(
r.Queue.Name,
&quot;&quot;,
&quot;logs&quot;,
false,
nil,
)
if err != nil {
log.Printf(&quot;failed to bind a queue&quot;)
}
log.Println(&quot;inside initqueue&quot;)
}
func (r *RabbitMQ) Consume() {
msgs, err := r.Channel.Consume(
r.Queue.Name,
&quot;&quot;,
true,
false,
false,
false,
nil,
)
if err != nil {
log.Printf(&quot;failed to register a consumer&quot;)
}
forever := make(chan bool)
go func() {
for {
select {
case msg, ok := &lt;-msgs:
if !ok {
log.Printf(&quot;something wrong&quot;)
return
}
log.Printf(&quot;Received message: [%v]\n&quot;, msg)
}
}
}()
log.Printf(&quot;Waiting for logs&quot;)
&lt;-forever
}

I am calling this function like:

rmq := shared.RabbitMQ{}
go rmq.New()
for {
go rmq.InitQueueForConsumer()
}

When I run I am getting the panic as follows:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x54 pc=0x11c27c6]
goroutine 22 [running]:
github.com/streadway/amqp.(*Channel).send(0x0, 0x1283c78, 0xc0000b0000, 0xc000071e28, 0x100e86a)
/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:157 +0x26
github.com/streadway/amqp.(*Channel).call(0x0, 0x1283c78, 0xc0000b0000, 0xc000071f40, 0x1, 0x1, 0xc000100050, 0x2)
/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:171 +0x5d
github.com/streadway/amqp.(*Channel).QueueDeclare(0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/Users/barisertas/go/pkg/mod/github.com/streadway/amqp@v1.0.0/channel.go:767 +0x150
github.com/bariis/rd/shared.(*RabbitMQ).InitQueueForConsumer(0xc000152000)
/Users/barisertas/workspace/golang-exercises/rd/shared/rabbitmq.go:151 +0x90
created by main.main
/Users/barisertas/workspace/golang-exercises/rd/receiver1/main.go:67 +0x76
exit status 2

And this what queueDeclare function returns from the original library:

func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
if err := args.Validate(); err != nil {
return Queue{}, err
}
req := &amp;queueDeclare{
Queue:      name,
Passive:    false,
Durable:    durable,
AutoDelete: autoDelete,
Exclusive:  exclusive,
NoWait:     noWait,
Arguments:  args,
}
res := &amp;queueDeclareOk{}
if err := ch.call(req, res); err != nil {
return Queue{}, err
}
if req.wait() {
return Queue{
Name:      res.Queue,
Messages:  int(res.MessageCount),
Consumers: int(res.ConsumerCount),
}, nil
}
return Queue{Name: name}, nil
}

I have tried every combination did with pointer, address etc. I tried to initialize it New function since it's called before QueueDeclare function I though It won't give panic but no solution. How can I do this properly?

答案1

得分: 2

rmq := shared.RabbitMQ{}RabbitMQ{} 中没有初始化任何字段。它为所有字段分配了零(默认)值。

指针的零(默认)值是nil。点击这里了解更多

因此,在 InitQueueForConsumer() 中,RabbitMQ{} 内部的所有指针都是nil。所以,当你在访问它们时,肯定会出现 runtime error: invalid memory address or nil pointer dereference 的错误。

在使用之前,请至少初始化指针字段。

通道的零(默认)值也是nil。参考 - 为什么Go语言中会有nil通道

但是,向/从nil通道发送/接收不会引发错误,而是永远阻塞。关闭一个nil通道会引发 runtime error: close of nil channel 的错误。

英文:

rmq := shared.RabbitMQ{} not initialising any fields in RabbitMQ{}. It assigns zero (default) values for all fields.

Zero (default) value of a pointer is nil. tour here

So all pointers are nil inside RabbitMQ{}. So, when you going to access them in InitQueueForConsumer(), sure it will panic with runtime error: invalid memory address or nil pointer dereference.

First Initialise atleast pointer fields before using them.

Zero (default) value of a channel is also nil. refer -why-are-there-nil-channels-in-go.

But send to/receive from a nil channel not panics, but blocks forever. Closing a nil channel will panic with runtime error: close of nil channel

huangapple
  • 本文由 发表于 2021年8月17日 22:02:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/68818796.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定