Go method call shorthand spec applicability

2019-06-06 06:39发布

From the Calls section of Go spec: https://golang.org/ref/spec#Calls

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

My program.

package main

import (
    "fmt"
)

func main() {
    p := Point{2, 3}

    p.FakeScale(10)
    fmt.Println(p)

    p.RealScale(10)
    fmt.Println(p)
}

type Point struct {
    x int
    y int
}

func (p Point) FakeScale(s int) {
    p.x *= s
    p.y *= s
}

func (p *Point) RealScale(s int) {
    p.x *= s
    p.y *= s
}

Here is the output.

{2, 3}
{20, 30}

My question is specifically about this part of the spec.

If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

My questions.

  1. Is the quoted part applicable to p.FakeScale(10)? My guess is "No" because although p is addressable, &p's method set does not contain FakeScale. p's method set contains FakeScale because it uses value receiver, but p's method set does not contain FakeScale, therefore this part of the spec is not applicable to p.FakeScale(10). Am I correct?
  2. Is the quoted part applicable to p.RealScale(10)? My guess is "Yes" because p is addressable and &p's method set contains RealScale by virtue of it using a pointer receiver. Therefore, this part of the spec is applicable to p.RealScale(10). Am I correct?
  3. Can you provide an example code where x.m() is valid but x is not addressable, and thus this part of the spec is not applicable?

2条回答
我想做一个坏孩纸
2楼-- · 2019-06-06 07:12

Hm. Also I begin to doubt my knowledge of English language but I will try to answer.

You quoted 2 sentences from specification.

  1. Describes conditions for method to be valid.
  2. Describes shorthand for recievers of pointer types.

Back to your question:

You defined 2 methods for 2 types: first - reciever of Point and second - receiver of pointer to Point.

Obviously, shorthand definition for pointer types is not applicable for non-pointer types.

So, you're correct in your questions 1 or 2.

And your third question is in fact question 1: FakeScale is valid method but method with non-pointer type in receiver, so shorthand definition is not applicable.

查看更多
smile是对你的礼貌
3楼-- · 2019-06-06 07:17

Unfortunately, language lawyers have to read the entire specification to put a single sentence in context: The Go Programming Language Specification.

Method sets

A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing anonymous fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.

The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.

Calls

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().

Address operators

For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal.


For example,

package main

import (
    "fmt"
)

type Point struct {
    x int
    y int
}

func (p Point) FakeScale(s int) {
    p.x *= s
    p.y *= s
}

func (p *Point) RealScale(s int) {
    p.x *= s
    p.y *= s
}

func main() {
    p := Point{2, 3}

    p.FakeScale(10)
    fmt.Println(p)

    p.RealScale(10)
    fmt.Println(p)
}

The method set of type Point consists of all methods declared with receiver type Point (FakeScale).

A method call p.FakeScale is valid since the method set of (the type of) p (Point) contains FakeScale and the argument list, (10), can be assigned to the parameter list, (int), of FakeScale.

The method set of type *Point consists of all methods declared with receiver type *Point (RealScale).

Since p is addressable (p is a variable) and &p's method set contains RealScale, p.RealScale() is shorthand for (&p).RealScale().


Also, see my answer to your earlier question: How to invoke a method with pointer receiver after type assertion?.

查看更多
登录 后发表回答