Ответ 1
Это зависит от приемника метода и от типа переменной.
Короткий ответ: если вы используете пакет database/sql
, ваш отложенный Rows.Close()
будут правильно закрывать оба ваших экземпляра Rows
, потому что Rows.Close()
имеет приемник указателей и, потому что DB.Query()
возвращает указатель (и поэтому Rows
является указателем). См. Объяснения и пояснения ниже.
Чтобы избежать путаницы, я рекомендую использовать разные переменные, и будет ясно, что вы хотите и что будет закрыто:
rows := Query(`SELECT FROM whatever`)
defer rows.Close()
// ...
rows2 := Query(`SELECT FROM whatever`)
defer rows2.Close()
Я хотел бы указать на важный факт, который исходит из отложенной функции и ее параметров, которые оцениваются непосредственно, что указано в Эффективный Go в блоге и в Language Spec: Отложенные утверждения тоже:
Каждый раз, когда выполняется оператор "defer", значение функции и параметры для вызова оцениваются как обычно, а сохраняется снова, но фактическая функция не вызывается. Вместо этого отложенные функции вызывают непосредственно перед возвратом функции окружающего объекта, в обратном порядке они откладываются.
Если переменная не является указателем: вы будете наблюдать разные результаты при вызове метода, отложенного, в зависимости от способа приема указателя-указателя.
Если переменная является указателем, вы всегда увидите "желаемый" результат.
См. этот пример:
type X struct {
S string
}
func (x X) Close() {
fmt.Println("Value-Closing", x.S)
}
func (x *X) CloseP() {
fmt.Println("Pointer-Closing", x.S)
}
func main() {
x := X{"Value-X First"}
defer x.Close()
x = X{"Value-X Second"}
defer x.Close()
x2 := X{"Value-X2 First"}
defer x2.CloseP()
x2 = X{"Value-X2 Second"}
defer x2.CloseP()
xp := &X{"Pointer-X First"}
defer xp.Close()
xp = &X{"Pointer-X Second"}
defer xp.Close()
xp2 := &X{"Pointer-X2 First"}
defer xp2.CloseP()
xp2 = &X{"Pointer-X2 Second"}
defer xp2.CloseP()
}
Вывод:
Pointer-Closing Pointer-X2 Second
Pointer-Closing Pointer-X2 First
Value-Closing Pointer-X Second
Value-Closing Pointer-X First
Pointer-Closing Value-X2 Second
Pointer-Closing Value-X2 Second
Value-Closing Value-X Second
Value-Closing Value-X First
Попробуйте на Go Playground.
Используя переменную указателя, результат всегда хорош (как и ожидалось).
Используя переменную non-pointer и используя приемник указателей, мы видим те же самые печатные результаты (последние), но если у нас есть приемник значений, он печатает 2 разных результата.
Объяснение для переменной без указателя:
Как указано, отложенная функция, включая приемник, оценивается при выполнении defer
. В случае приемника указателя он будет адресом локальной переменной. Поэтому, когда вы назначаете ему новое значение и вызываете другой defer
, получатель указателя будет снова тем же адресом локальной переменной (только указанное значение отличается). Таким образом, позже, когда функция будет выполнена, оба будут использовать один и тот же адрес дважды, но указанное значение будет таким же, как и указанное позже.
В случае приемника значения приемник представляет собой копию, которая выполняется при выполнении defer
, поэтому, если вы назначаете новое значение переменной и вызываете другую defer
, другая копия будет отличаться от предыдущего.