Если resp
является указателем на объект ответа, почему к самому [объекту] можно обратиться, используя [fmt.Println(resp)
] ... Разве это не должно быть fmt.Println(*resp)
вместо этого?
Если вы отправите на fmt.Println
указатель на объект, fmt.Println
может использовать указатель, чтобы достичь самого объекта (то есть получить к нему доступ и даже изменить его, но fmt.Println
не изменит его) .
Если вы отправите fmt.Println
копию объекта, fmt.Println
может использовать копию объекта, то есть получить к ней доступ (и не сможет изменить оригинал).
Таким образом, в этом смысле предоставление fmt.Println
значения указателя является строго более мощным, чем передача копии объекта, поскольку он может изменить объект. Код fmt
не использует эту мощность , но он есть в любом другом месте, где вы также можете передать указатель. Но пока fmt.Println
:
- замечает, что это указатель, а затем
- следует за указателем для доступа к базовому объекту,
тогда fmt.Println
может вести себя одинаково как для указателя на объект, так и для копии объекта.
На самом деле, семейство функций fmt.Print*
не совсем ведет себя точно так же с указателем на объект и копией объекта:
package main
import (
"fmt"
)
type T struct {
Name string
Value int
}
func main() {
obj := T{Name: "bob", Value: 42}
fmt.Println(&obj, obj)
fmt.Printf("%#v %#v\n", &obj, obj)
}
При запуске ( попробуйте на Go Playground ), он печатает:
&{bob 42} {bob 42}
&main.T{Name:"bob", Value:42} main.T{Name:"bob", Value:42}
То есть форматирование по умолчанию, которое вы получаете с помощью %v
или fmt.Println
, печатает либо:
{bob 42}
(копия объекта), либо:
&{bob 42}
(указатель на объект). Альтернативный формат, полученный с помощью %#v
, добавляет тип, так что вы получаете либо:
main.T{Name:"bob", Value:42}
(копия объекта), либо:
&main.T{Name:"bob", Value:42}
Здесь мы видим, что fmt.Println
, который принимает значение interface{}
, проходит следующий процесс:
- Проверьте тип значения. Это указатель? Если это так, помните, что это был указатель. Напечатайте
<nil>
и больше не go, если это нулевой указатель; в противном случае получите объект, на который указывает указатель. Теперь, когда это не указатель: какой тип имеет значение? Если это тип struct
, распечатайте его имя типа (%#v
) или нет (%v
), с префиксом &
, если за шагом 1 следует указатель, а затем открывающая фигурная скобка и список значений вещи внутри структуры, а затем закрывающая скобка для завершения всего.
При использовании %#v
выведите имена полей и напечатайте значения в формате, подходящем для использования в качестве исходного кода Go , В противном случае просто напечатайте содержимое строк и int
с и т. Д.
Другие типы указателей не всегда обрабатываются одинаково! Например, добавьте переменную int
, установите для нее какое-либо значение и вызовите fmt.Println(&i, i)
. Обратите внимание, что в этот раз вы получите не &42 42
или что-то в этом роде, а 0x40e050 42
или что-то в этом роде. Попробуйте это с fmt.Printf
и %#v
. Поэтому вывод зависит от типа и глагола форматирования.
Если вы вызываете функции, которые должны изменить их объекты (например, семейство scan
в fmt
), вы должен передать указатель, так как им нужен доступ к объектам для их изменения.
Каждая функция, которая может принимать значения неограниченных типов interface{}
(включая все в Print*
и Scan*
семья здесь) должны документировать, что они делают с каждым фактическим типом. Если они говорят, как это делает семья Print*
, то, когда они получают указатель на тип структуры, они следуют за указателем (если не ноль), что позволяет вам знать, что вы можете отправить указатель вместо объекта.
(Некоторые функции в некоторых библиотеках виновны в недостаточном документировании того, что они делают, и вам приходится экспериментировать. В общем, это не очень хорошая ситуация, потому что результаты эксперимента могут быть случайностью текущей реализации, а не обещанное поведение, которое не изменится в будущем. Это одна из причин, по которой стоит позаботиться о использовании interface{}
: это означает, что вам нужно написать много документации.)