Json.Unmarshal () не будет работать, если вы передадите нулевой указатель, но сработает, если вы передадите указатель нулевого указателя. Любопытный?

Я вам скажу почему.

Я покажу вам один интересный фрагмент кода, включающий Unmarshal(), и посмотрю, сможете ли вы правильно угадать результат.

Unmarshal ожидает, что второй аргумент будет иметь тип указателя и не равен нулю.

В приведенном выше отрывке в обе Unmarshal() функции мы передаем указатели. В Ln 17 - указатель на указатель, а в Ln 23 - указатель. Однако работает только первый демаршал.

Знаешь, почему он запаниковал?

Первая строка документа Unmarshal гласит:

Unmarshal анализирует данные в кодировке JSON и сохраняет результат в значении, на которое указывает v.

Память, на которую указывают res1 и res2, не инициализирована, т.е. равна нулю. Мы передали res2 напрямую Unmarshal(), и теперь мы понимаем, почему он запаниковал, говоря (nil *main.Result). res2 ни на что не указывает.

В первом случае передается адрес res1, где res1 - нулевой указатель, то есть указатель ни на что. Unmarshal() получает копию адреса res1, скажем, copyOfRes1.

Внутренне Unmarshal() разыменовывает copyOfRes1 в res1 и декодирует значение в память, на которую указывает res1. Поскольку res1 является указателем nil, создается новое значение Result, и его адрес сохраняется в res1, т.е. теперь res1 указывает на вновь созданный объект Result. Из документа Unmarshal:

Unmarshal демаршалирует JSON в значение, на которое указывает указатель. Если указатель равен нулю, Unmarshal выделяет для него новое значение.

Если бы та же самая логика произошла во втором случае, копия res2 с именем copyOfRes2 передается в Unmarshal(). copyOfRes2, нулевой указатель, будет перезаписан новым адресом, который указывает на вновь созданный объект Result. Однако, поскольку передается только копия res2, основная программа не увидит ее, потому что значения res2 внутри основной программы и Unmarshal() различны. В конце концов, это бесполезно, верно? Вот почему Unmarshal() внутренне проверяет, имеет ли copyOfRes2 нулевое значение в начале, а ошибка возникает из-за this!

Чтобы добавить, следующее работает отлично, потому что Unmarshal() проходит по нескольким уровням косвенных ссылок, пока не встретит не-указатель.

Мой анализ

  1. Https://twitter.com/fatih/status/1260683136842608640
  2. Https://dave.cheney.net/2020/05/16/fatihs-question

Думаю, я достаточно хорошо рассудил. Прокомментируйте свои вопросы.

Хлопайте, если хотите, поделитесь, если вам не все равно :)