Golang中指针方法如何接收值接收器

Golang中指针方法如何接收值接收器 我创建了一个只接受指针的方法,但为什么我仍然能够通过值接收器调用它?

type User struct {
Name string
Email string
}

 func (u *User) Notify() {
fmt.Println(u.Name)
}

func main() {
     // 尽管是值类型,但工作正常
bill := User{"Bill", "bill@email.com"}
bill.Notify()


jill := &User{"Jill", "jill@email.com"}
jill.Notify()
}

但是当我添加接口时:

package main


 func (u *User) Notify() error {
log.Printf("User: Sending User Email To %s<%s>\n",
	u.Name,
	u.Email)

return nil
}

type Notifier interface {
Notify() error
}

func SendNotification(notify Notifier) error {
return notify.Notify()
}

 func main() {
user := User{
	Name:  "janet jones",
	Email: "janet@email.com",
}

SendNotification(user)
}

它开始抛出错误,说我只接受指针,这是为什么???


更多关于Golang中指针方法如何接收值接收器的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

https://www.reddit.com/r/golang/comments/8fjpjh/why_mutating_a_value_receiver_accepted_by_a/ 对此进行了部分讨论。

相关内容:

如果方法接收指针类型,那么 value.Method()(&value).Method() 的语法糖

反之,如果方法接收值类型,那么 pointer.Method()(*pointer).Method() 的语法糖

换句话说,你可以直接写成 thing.Method(),无论哪种情况几乎都能正常工作。

至于为什么这种语法糖不适用于接口——我不太确定具体原因。你可能需要询问语言设计时的决策者,但我的猜测是为了避免混淆。

首先,不清楚 SendNotification(user) 应该传递 user 的副本并在该副本上调用指针方法,还是应该传递指针而不是你实际输入的内容。对于前者,你的"指针方法"实际上并没有改变传递给 SendNotification 的用户,这可能会让人困惑。对于后者,编译器会修改你的代码,将 &user 传递给 SendNotification 函数,这同样非常不明确且令人困惑。

更多关于Golang中指针方法如何接收值接收器的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是Go语言中方法集(method set)的规则导致的。让我详细解释一下:

方法集规则

在Go中,方法集决定了哪些方法可以被特定类型的值调用:

  • 类型 T 的方法集包含所有 值接收器 声明的方法
  • 类型 *T 的方法集包含所有 值接收器指针接收器 声明的方法

你的代码分析

第一个例子能工作的原因

bill := User{"Bill", "bill@email.com"}
bill.Notify()  // 这能工作

这里Go编译器自动做了转换,等价于:

(&bill).Notify()

第二个例子失败的原因

func SendNotification(notify Notifier) error {
    return notify.Notify()
}

user := User{
    Name:  "janet jones",
    Email: "janet@email.com",
}

SendNotification(user)  // 编译错误!

当通过接口调用时,Go不会自动取地址。User 类型的方法集只包含值接收器的方法,而 Notify() 是指针接收器的方法,所以 User 不满足 Notifier 接口。

解决方案

方案1:传递指针

func main() {
    user := User{
        Name:  "janet jones",
        Email: "janet@email.com",
    }
    
    SendNotification(&user)  // 传递指针
}

方案2:使用值接收器

func (u User) Notify() error {  // 改为值接收器
    log.Printf("User: Sending User Email To %s<%s>\n",
        u.Name,
        u.Email)
    return nil
}

方案3:直接创建指针

func main() {
    user := &User{  // 直接创建指针
        Name:  "janet jones",
        Email: "janet@email.com",
    }
    
    SendNotification(user)
}

完整工作示例

package main

import (
    "log"
)

type User struct {
    Name  string
    Email string
}

func (u *User) Notify() error {
    log.Printf("User: Sending User Email To %s<%s>\n",
        u.Name,
        u.Email)
    return nil
}

type Notifier interface {
    Notify() error
}

func SendNotification(notify Notifier) error {
    return notify.Notify()
}

func main() {
    user := User{
        Name:  "janet jones",
        Email: "janet@email.com",
    }
    
    // 正确调用方式
    SendNotification(&user)
}

记住这个规则:接口调用时不会自动取地址,直接方法调用时会自动转换

回到顶部