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

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

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

问题

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

  1. type RabbitMQ struct {
  2. Connection *amqp.Connection
  3. Channel *amqp.Channel
  4. Queue amqp.Queue // 用于消费者
  5. done chan os.Signal
  6. notifyClose chan *amqp.Error
  7. notifyConfirm chan *amqp.Confirmation
  8. IsConnected bool
  9. alive bool
  10. Done chan os.Signal
  11. Err chan error
  12. Wg *sync.WaitGroup
  13. }

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

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

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

  1. func (r *RabbitMQ) InitQueueForConsumer() {
  2. log.Println("inside initqueue")
  3. var err error
  4. r.Queue, err = r.Channel.QueueDeclare(
  5. "", // 名称
  6. false, // 持久化
  7. false, // 未使用时删除
  8. true, // 独占
  9. false, // 不等待
  10. nil, // 参数
  11. )
  12. if err != nil {
  13. log.Printf("failed to declare a queue")
  14. }
  15. log.Println("inside initqueue")
  16. err = r.Channel.QueueBind(
  17. r.Queue.Name,
  18. "",
  19. "logs",
  20. false,
  21. nil,
  22. )
  23. if err != nil {
  24. log.Printf("failed to bind a queue")
  25. }
  26. log.Println("inside initqueue")
  27. }
  28. func (r *RabbitMQ) Consume() {
  29. msgs, err := r.Channel.Consume(
  30. r.Queue.Name,
  31. "",
  32. true,
  33. false,
  34. false,
  35. false,
  36. nil,
  37. )
  38. if err != nil {
  39. log.Printf("failed to register a consumer")
  40. }
  41. forever := make(chan bool)
  42. go func() {
  43. for {
  44. select {
  45. case msg, ok := <-msgs:
  46. if !ok {
  47. log.Printf("something wrong")
  48. return
  49. }
  50. log.Printf("Received message: [%v]\n", msg)
  51. }
  52. }
  53. }()
  54. log.Printf("Waiting for logs")
  55. <-forever
  56. }

我像这样调用这个函数:

  1. rmq := shared.RabbitMQ{}
  2. go rmq.New()
  3. for {
  4. go rmq.InitQueueForConsumer()
  5. }

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

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

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

  1. func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
  2. if err := args.Validate(); err != nil {
  3. return Queue{}, err
  4. }
  5. req := &queueDeclare{
  6. Queue: name,
  7. Passive: false,
  8. Durable: durable,
  9. AutoDelete: autoDelete,
  10. Exclusive: exclusive,
  11. NoWait: noWait,
  12. Arguments: args,
  13. }
  14. res := &queueDeclareOk{}
  15. if err := ch.call(req, res); err != nil {
  16. return Queue{}, err
  17. }
  18. if req.wait() {
  19. return Queue{
  20. Name: res.Queue,
  21. Messages: int(res.MessageCount),
  22. Consumers: int(res.ConsumerCount),
  23. }, nil
  24. }
  25. return Queue{Name: name}, nil
  26. }

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

英文:

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

  1. type RabbitMQ struct {
  2. Connection *amqp.Connection
  3. Channel *amqp.Channel
  4. Queue amqp.Queue // for consumer
  5. done chan os.Signal
  6. notifyClose chan *amqp.Error
  7. notifyConfirm chan *amqp.Confirmation
  8. IsConnected bool
  9. alive bool
  10. Done chan os.Signal
  11. Err chan error
  12. Wg *sync.WaitGroup
  13. }

This is the original Queue struct inside original library.

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

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

  1. func (r *RabbitMQ) InitQueueForConsumer() {
  2. log.Println(&quot;inside initqueue&quot;)
  3. var err error
  4. r.Queue, err = r.Channel.QueueDeclare(
  5. &quot;&quot;, // name
  6. false, // durable
  7. false, // delete when unused
  8. true, // exclusive
  9. false, // no-wait
  10. nil, // arguments
  11. )
  12. if err != nil {
  13. log.Printf(&quot;failed to declare a queue&quot;)
  14. }
  15. log.Println(&quot;inside initqueue&quot;)
  16. err = r.Channel.QueueBind(
  17. r.Queue.Name,
  18. &quot;&quot;,
  19. &quot;logs&quot;,
  20. false,
  21. nil,
  22. )
  23. if err != nil {
  24. log.Printf(&quot;failed to bind a queue&quot;)
  25. }
  26. log.Println(&quot;inside initqueue&quot;)
  27. }
  28. func (r *RabbitMQ) Consume() {
  29. msgs, err := r.Channel.Consume(
  30. r.Queue.Name,
  31. &quot;&quot;,
  32. true,
  33. false,
  34. false,
  35. false,
  36. nil,
  37. )
  38. if err != nil {
  39. log.Printf(&quot;failed to register a consumer&quot;)
  40. }
  41. forever := make(chan bool)
  42. go func() {
  43. for {
  44. select {
  45. case msg, ok := &lt;-msgs:
  46. if !ok {
  47. log.Printf(&quot;something wrong&quot;)
  48. return
  49. }
  50. log.Printf(&quot;Received message: [%v]\n&quot;, msg)
  51. }
  52. }
  53. }()
  54. log.Printf(&quot;Waiting for logs&quot;)
  55. &lt;-forever
  56. }

I am calling this function like:

  1. rmq := shared.RabbitMQ{}
  2. go rmq.New()
  3. for {
  4. go rmq.InitQueueForConsumer()
  5. }

When I run I am getting the panic as follows:

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

And this what queueDeclare function returns from the original library:

  1. func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
  2. if err := args.Validate(); err != nil {
  3. return Queue{}, err
  4. }
  5. req := &amp;queueDeclare{
  6. Queue: name,
  7. Passive: false,
  8. Durable: durable,
  9. AutoDelete: autoDelete,
  10. Exclusive: exclusive,
  11. NoWait: noWait,
  12. Arguments: args,
  13. }
  14. res := &amp;queueDeclareOk{}
  15. if err := ch.call(req, res); err != nil {
  16. return Queue{}, err
  17. }
  18. if req.wait() {
  19. return Queue{
  20. Name: res.Queue,
  21. Messages: int(res.MessageCount),
  22. Consumers: int(res.ConsumerCount),
  23. }, nil
  24. }
  25. return Queue{Name: name}, nil
  26. }

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:

确定