Golang中为什么使用&struct而不是struct

Golang中为什么使用&struct而不是struct

func main() {
    p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
    p1.save()
    p2, _ := loadPage("TestPage")
    fmt.Println(string(p2.Body))
}

这段代码是 Go 语言教程中“编写 Web 应用程序”章节的一部分。我的问题是,为什么在上面的代码片段中我们使用 &Page 而不是直接使用 Page

5 回复

感谢,我的问题已经解决了。

更多关于Golang中为什么使用&struct而不是struct的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


没有理由。在这种情况下,即使没有 & 符号,效果也是一样的。

通常,指针比对象本身更有用。你可以执行空值检查,并通过函数接收器设置对象属性,而不是直接修改属性。

然而,没有对象指针的对象也是通过引用传递的,这意味着你也可以直接修改它们的属性,但不是通过函数接收器,同时你也无法进行空值检查。

一个通用的经验法则是创建 NewApple() *apple { return &apple{} },这样当你有一个接口时,你可以轻松地将其修改为 NewApple() Fruit {return &apple{} }。请记住,这对于你的小型代码库来说并非必需。

func main() {
    fmt.Println("hello world")
}

通常,指针比对象本身更有用。你可以通过函数接收器进行 nil 检查并设置对象属性,而不是直接更改属性。

然而,没有对象指针的对象也是通过引用传递的,这意味着你也可以直接更改它们的属性,但不是通过函数接收器,你也不能进行 nil 检查。

一个通用的经验法则是创建 NewApple() *apple { return &apple{} },这样当你有一个接口时,你可以轻松地将其修改为 NewApple() Fruit {return &apple{} },请记住这对于你的小型代码库来说不是必需的。

感谢提供这么棒的信息。

在Go语言中,使用&Page而不是Page主要涉及内存效率和数据一致性两个关键因素。以下是具体分析:

1. 避免值拷贝,提升性能

当传递Page结构体时,会发生完整的值拷贝(包括TitleBody字段)。对于包含较大[]byte数据的结构体,拷贝开销显著:

// 值传递示例(产生拷贝开销)
func (p Page) saveByValue() {
    // 每次调用都会复制整个Page结构体
    filename := p.Title + ".txt"
    ioutil.WriteFile(filename, p.Body, 0600)
}

// 指针传递示例(避免拷贝)
func (p *Page) save() {
    filename := p.Title + ".txt"
    ioutil.WriteFile(filename, p.Body, 0600)
}

2. 确保方法能修改接收者状态

教程中的save()方法需要将数据持久化到文件。如果使用值接收者,方法内修改不会影响原结构体:

type Page struct {
    Title string
    Body  []byte
}

// 错误示例:值接收者无法修改原结构体
func (p Page) updateTitle(newTitle string) {
    p.Title = newTitle // 仅修改副本
}

// 正确示例:指针接收者可修改原结构体
func (p *Page) updateTitle(newTitle string) {
    p.Title = newTitle // 修改原对象
}

func main() {
    p := &Page{Title: "Old"}
    p.updateTitle("New")
    fmt.Println(p.Title) // 输出"New"
}

3. 与接口实现的一致性

当结构体需要实现接口时,指针接收者能保持方法集的统一:

type Saver interface {
    save()
}

// 只有*Page实现了Saver接口
func (p *Page) save() { /* ... */ }

func main() {
    var s Saver
    p := &Page{Title: "Test"}
    s = p // 正确:*Page实现了Saver
    // s = Page{Title: "Test"} // 编译错误:Page未实现Saver
}

4. 教程代码的具体分析

在Web应用场景中,loadPage()返回*Page指针是为了:

func loadPage(title string) (*Page, error) {
    filename := title + ".txt"
    body, _ := ioutil.ReadFile(filename)
    return &Page{Title: title, Body: body} // 返回指针避免拷贝
}

// 调用方直接使用指针,避免二次拷贝
p2, _ := loadPage("TestPage")
p2.save() // 无需解引用,Go自动处理

5. 内存布局对比

// 值类型:每次传递产生新副本
func processPage(p Page) { /* 操作副本 */ }

// 指针类型:传递固定大小的地址(通常8字节)
func processPagePtr(p *Page) { /* 操作原对象 */ }

结论:教程使用&Page是为了:

  • 减少大型结构体的拷贝开销
  • 允许方法修改接收者状态
  • 保持与接口实现的一致性
  • 符合Go中“共享通过指针,拷贝通过值”的惯用法

在Web开发中,这种模式尤为重要,因为HTTP处理器需要高效处理大量请求,避免不必要的内存拷贝能显著提升性能。

回到顶部