V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lueffy
V2EX  ›  Go 编程语言

A Tour of Go-Pointer receivers 一点疑问

  •  
  •   lueffy · 2019-05-13 11:48:47 +08:00 · 2245 次点击
    这是一个创建于 2027 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://tour.golang.org/methods/4

    刚开始学 go,从 A Tour of Go 开始看的,求大佬帮忙看看,非常感谢

    package main
    
    import (
    	"fmt"
    	"math"
    )
    
    type Vertex struct {
    	X, Y float64
    }
    
    func (v Vertex) Abs() float64 {
    	return math.Sqrt(v.Xv.X + v.Yv.Y)
    }
    
    func (v *Vertex) Scale(f float64) { //这里 receiver 的类型是 指针类型
    	v.X = v.X * f
    	v.Y = v.Y * f
    }
    
    func main() {
    	v := Vertex{3, 4}
    	v.Scale(10) //但是 v 是 Vertex 类型啊?为什么也能运行通过?
    	fmt.Println(v.Abs())
    }
    
    package main
    
    import (
    	"fmt"
    	"math"
    )
    
    type Vertex struct {
    	X, Y float64
    }
    
    func (v Vertex) Abs() float64 {
    	return math.Sqrt(v.Xv.X + v.Yv.Y)
    }
    
    func (v *Vertex) Scale(f float64) {
    	v.X = v.X * f
    	v.Y = v.Y * f
    }
    
    func main() {
    	v := Vertex{3, 4}
    	var m *Vertex
    	m = &v //我理解的应该是这样的
    	m.Scale(10)
    	fmt.Println(v.Abs())
    }
    
    
    8 条回复    2019-05-13 23:55:15 +08:00
    heimeil
        1
    heimeil  
       2019-05-13 11:57:35 +08:00 via Android   ❤️ 1
    编译器自动帮你做了你想到的步骤
    icexin
        2
    icexin  
       2019-05-13 11:58:51 +08:00   ❤️ 1
    为了写代码方便。否则第一个例子你得这么写(&v).Scale(10),go 默认帮你加上了取地址符,前提是变量本身是可以取地址的。
    如果你写了一个签名为 func GetVertex() Vertex 的函数,GetVertex().Scale(10)就会失败,因为返回的临时变量不可取地址。
    bef0rewind
        3
    bef0rewind  
       2019-05-13 12:17:13 +08:00 via iPhone   ❤️ 1
    https://golang.org/ref/spec#Method_values

    As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.

    ```go
    f := t.Mv; f(7) // like t.Mv(7)
    f := pt.Mp; f(7) // like pt.Mp(7)
    f := pt.Mv; f(7) // like (*pt).Mv(7)
    f := t.Mp; f(7) // like (&t).Mp(7)
    f := makeT().Mp // invalid: result of makeT() is not addressable
    ```

    Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.

    ```go
    var i interface { M(int) } = myVal
    f := i.M; f(7) // like i.M(7)
    ```
    lueffy
        4
    lueffy  
    OP
       2019-05-13 13:46:34 +08:00
    反过来就不行

    package main

    import (
    "fmt"
    "math"
    )

    type Vertex struct {
    X, Y float64
    }

    func Abs(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }

    func Scale(v Vertex, f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
    }

    func main() {
    v := Vertex{3, 4}
    Scale(&v, 10)
    fmt.Println(Abs(v))
    }

    cannot use &v (type *Vertex) as type Vertex in argument to Scale
    liangjf
        5
    liangjf  
       2019-05-13 14:32:41 +08:00
    编译器自己加了。。。
    bef0rewind
        6
    bef0rewind  
       2019-05-13 14:40:38 +08:00 via iPhone
    @lueffy 你的例子错了,这个报错是说参数类型不匹配。
    lueffy
        7
    lueffy  
    OP
       2019-05-13 14:54:53 +08:00
    @bef0rewind 是,评论那个是函数(参数类型不对),不是方法(接收者类型),我搞混淆了
    liulaomo
        8
    liulaomo  
       2019-05-13 23:55:15 +08:00
    @lueffy 反过来也是可以的

    package main

    import (
    "fmt"
    "math"
    )

    type Vertex struct {
    X, Y float64
    }

    func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
    }

    func main() {
    v := Vertex{3, 4}
    fmt.Println((&v).Abs())
    }

    因为编译器给指针类型*Vertex 隐式声明了一个同名方法。https://gfw.go101.org/article/method.html#implicit-pointer-methods
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1014 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 19:42 · PVG 03:42 · LAX 11:42 · JFK 14:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.