Golang中在返回未被修改的变量前如何解引用

Golang中在返回未被修改的变量前如何解引用 你好,

由于 caller.go 永远不需要修改 result 变量,我相信我在 result.go 中对 result 变量进行解引用的做法是“好”的。我这样假设正确吗?这是在阅读了 https://github.com/golang/go/wiki/CodeReviewComments#receiver-type 后得出的结论。

另外,在 Go 语言中解引用(使用 * 操作符)并不会创建副本。它返回的是指针所指向的值。这意味着它不会在内存中分配额外的空间。这样理解对吗?

谢谢

// result.go
func NewResult(user entity.User) Result {
	result := &Result{}

    // 根据 `user` 更新 `result` 的所有属性。
    // result.... = user....
    
	return *result
}
// caller.go
func main() {
    result := result.NewResult(User{//....})

    // 对 `result` 进行一些操作,但不修改它。
    
    fmt.Println(result)
}

更多关于Golang中在返回未被修改的变量前如何解引用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

这是一个替代解决方案,我认为它可以避免复制,除非编译器已经对此进行了优化。

func NewResult(user entity.User) (result Result) {
    // 更新 `result` 中所有来自 `user` 的属性。
    // result.... = user....
    return
}

更多关于Golang中在返回未被修改的变量前如何解引用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


@Christophe_Meessen @calmh

感谢提供宝贵的意见,所有这些都令人感激,帮助解答并澄清了疑问。

我之所以提出这个特定问题,主要原因是,我经常看到人们默认地在函数间传递或返回指针,即使他们并不修改其指向的值。作为一名学习者,我目前还无法对此提出强有力的反对论点,但我感觉,到处盲目地过度使用指针是一种有点不好的习惯。我认为大多数人遵循的是“如有疑问,使用指针”这一条,正如这里所提到的——但实际上这句话应该解读为“如有疑问,说明你并不知道自己在做什么” 😬

在 Go 中,你的理解基本正确。当返回 *result 时,确实会解引用指针并返回结构体的值副本。这不会创建额外的指针内存分配,但会复制整个结构体。

示例代码:

// result.go
type Result struct {
    ID   int
    Name string
}

func NewResult(user entity.User) Result {
    result := &Result{
        ID:   user.ID,
        Name: user.Name,
    }
    // 解引用返回结构体值
    return *result
}

// caller.go
func main() {
    user := entity.User{ID: 1, Name: "John"}
    result := NewResult(user)
    // result 现在是值类型,调用者无法修改原始数据
    fmt.Printf("%+v\n", result)
}

关于内存:解引用操作 *result 返回的是结构体的值,这个值会被复制到调用者的栈帧中。指针本身的内存(&Result{} 分配的内存)在函数返回后会被垃圾回收器回收,因为不再有引用指向它。

这种模式适用于:

  1. 调用者不需要修改返回的数据
  2. 结构体较小,复制开销可接受
  3. 希望保持 API 的不可变性

如果结构体很大,建议返回指针以避免复制开销,即使调用者不需要修改数据。Go 的逃逸分析通常会将这种小结构体分配在栈上,性能影响很小。

那样做是可行的,但我通常会:

func NewResult(user entity.User) Result {
    var result Result

    // 更新 `result` 中所有来自 `user` 的属性。
    // result.... = user....
    
	return result
}

省略指针,或者直接:

func NewResult(user entity.User) Result {
    return Result{
        // 所有属性以 Field: value 的形式列出
    }
}

如果字段值不需要计算等操作,并且这样写看起来清晰的话。

Also dereferencing (using the * operator) in Go doesn’t make a copy. It returns the value the pointer points to. This means it doesn’t allocate more space in memory. Is that correct?

也许吧?我的意思是,* 操作本身可能不会复制,但结果需要存放到某个地方。当返回结构体时,它需要在栈上。该栈空间由调用者分配并清零,因此我们需要将我们的结构体复制到其中。(你可以将返回值本身视为一个特殊的变量,其中 return result 类似于 preallocatedReturnValue = result。)我猜测上述所有代码片段都会导致一次内存复制(栈到栈),但内存复制成本非常低。它们都不应该导致需要避免的堆分配,这很好。编译器也有可能对此进行优化,在上述所有情况下直接写入返回值,但我不会指望这一点。而且无论如何这真的不重要。slight_smile

回到顶部