Go Синтаксис вызова функции с указателем получателя - PullRequest
3 голосов
/ 31 марта 2012

В Go, если я определю функцию с указателем в качестве получателя, не должен ли он разрешить вызов функции только из указателя? Почему нормально вызывать эту функцию из самого значения и иметь тот же эффект?

Например, в следующей программе: m1.reset () & m2.reset () имеют одинаковый эффект. Даже если m1 является значением, а m2 является указателем.

Я немного запутался, поскольку есть два способа сделать одно и то же , и я не уверен, какой из них следовать. Хотя большая часть кода соответствует соглашению о вызове функции с использованием поля указателя. Я что-то упустил?

package main

    import "fmt"

    type MyStruct struct {
        X int
    }

    func (m *MyStruct) reset() {
        m.X = 0
    }

    func main() {
        m1 := MyStruct{1}
        m2 := &MyStruct{1}

        fmt.Println(m1.X)
        fmt.Println(m2.X)

        m1.reset()
        m2.reset()

        fmt.Println(m1.X)
        fmt.Println(m2.X)
    }

Ответы [ 3 ]

9 голосов
/ 31 марта 2012

@ jnml предлагает идеальное объяснение спецификации документа, но я хотел добавить пример кода на основе вашего. Я думаю, что вы должны сосредоточиться не столько на том, «Почему есть два способа сделать одно и то же», а на том, когда использовать один против другого. Метод, имеющий указатель в качестве получателя, имеет возможность изменять значения этого получателя, в то время как метод, который имеет значение в качестве получателя, не может. Это происходит потому, что методы получают копию получателя. Когда вы получаете копию указателя, вы все равно можете изменить его значение. Когда вы получаете копию значения, изменения, которые вы вносите в этом методе, изменяют только копию, а не оригинал:

package main

import "fmt"

type MyStruct struct {
    X int
}

func (m *MyStruct) resetPtr() {
    m.X = 0
}

func (m MyStruct) resetValue() {
    m.X = 0
}

func main() {
    m1 := MyStruct{1}
    m2 := &MyStruct{1}

    fmt.Println("Original Values:", m1.X, m2.X)

    m1.resetPtr()
    m2.resetPtr()

    fmt.Println("After resetPtr():", m1.X, m2.X)

    m1 = MyStruct{1}
    m2 = &MyStruct{1}

    m1.resetValue()
    m2.resetValue()

    fmt.Println("After resetValue():", m1.X, m2.X)
}

выход

Original Values: 1 1
After resetPtr(): 0 0
After resetValue(): 1 1

Вы видите, что способ доступа к этим переменным на самом деле не является проблемой. Больше о том, что вы можете делать с ними внутри метода, и как они передаются в качестве аргументов другим функциям или методам (копируются).

4 голосов
/ 31 марта 2012

Спецификации говорит :

Набор методов соответствующего типа указателя * T - это набор всех методов с приемником * T или T (то есть он также содержитнабор методов T).

Следующая необходимая информация о вызовах методов говорит:

Вызов метода x.m() действителен, если набор методов (type of) x содержит m, и список аргументов можно назначить списку параметров m.Если x адресуемый и набор методов & x содержит m, x.m() является сокращением для (&x).m().

Соедините две вышеупомянутые вещи вместе, и вы получите поведение, которое видите.

2 голосов
/ 31 марта 2012

Краткое объяснение состоит в том, что компилятор Go за кадром автоматически преобразует:

m1.reset()
m2.reset()

в

(&m1).reset()
m2.reset()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...