Как использовать структуру внутри структуры? разыменование недопустимого адреса памяти или указателя с нулевым значением

# #go #pointers #struct #rabbitmq

Вопрос:

У меня возникает паника, когда я хочу использовать Queue . Это моя структура:

 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
}
 

Это оригинальная Queue структура внутри оригинальной библиотеки.

 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
}
 

Здесь, в этой функции, я пытаюсь использовать r.Queue :

 func (r *RabbitMQ) InitQueueForConsumer() {
    log.Println("inside initqueue")
    var err error
    r.Queue, err = r.Channel.QueueDeclare(
        "",    // name
        false, // durable
        false, // delete when unused
        true,  // exclusive
        false, // no-wait
        nil,   // arguments
    )
    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 := 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
}
 

Я перепробовал все комбинации с указателем, адресом и т. Д. Я попытался инициализировать его New функцию, так как он вызывается до QueueDeclare функции I, хотя это не вызовет паники, но не решит проблему. Как я могу сделать это правильно?

Комментарии:

1. Я не вижу кода, который создал соединение и канал из него.

Ответ №1:

rmq := shared.RabbitMQ{} не инициализируя никаких полей внутри RabbitMQ{} . Он присваивает нулевые значения (по умолчанию) для всех полей.

Нулевое (по умолчанию) значение указателя равно нулю. экскурсия здесь

Таким образом, все указатели внутри равны нулю RabbitMQ{} . Так что, когда вы собираетесь получить к ним доступ InitQueueForConsumer() , обязательно запаникуйте runtime error: invalid memory address or nil pointer dereference .

Сначала инициализируйте по крайней мере поля указателя, прежде чем использовать их.

Нулевое (по умолчанию) значение канала также равно нулю. обратитесь —почему-нет-каналов-в-ходу.

Но отправка/получение с нулевого канала не паникует, а блокируется навсегда. Закрытие нулевого канала вызовет панику с runtime error: close of nil channel