0

0

如何在 Go 中正确选择嵌入结构体时使用值还是指针

霞舞

霞舞

发布时间:2026-01-05 12:58:02

|

974人浏览过

|

来源于php中文网

原创

如何在 Go 中正确选择嵌入结构体时使用值还是指针

go 中嵌入结构体时,应根据是否需要共享状态、避免拷贝开销或支持运行时动态替换来决定使用值嵌入还是指针嵌入;`log.logger` 等非接口类型两者皆可,但指针嵌入更灵活、更符合常见实践。

Go 的嵌入(embedding)机制是实现组合与方法提升(method promotion)的核心特性。当你将一个类型作为匿名字段嵌入到另一个结构体中时,该类型的方法会自动提升为外层结构体的方法——但这一机制对嵌入类型的形态有明确限制:根据 Go 语言规范,嵌入字段必须是*具名类型 T 或其指针 `T**,且T不能是指针类型(即不支持*T或interface{})。由于log.Logger` 是一个具体结构体(非接口),因此以下两种写法在语法和语义上均合法:

// ✅ 合法:嵌入值(拷贝语义)
type Job struct {
    Command string
    log.Logger // 值嵌入
}

// ✅ 合法:嵌入指针(引用语义)
type Job struct {
    Command string
    *log.Logger // 指针嵌入
}

然而,语义差异显著

  • 值嵌入(log.Logger):每次创建 Job 实例时,都会完整拷贝一份 Logger 内部状态(如 mu sync.Mutex, out io.Writer, prefix, flag 等)。这不仅带来不必要的内存开销,还可能导致并发安全问题(如多个 Job 实例各自持有独立的 Mutex,无法协同保护共享资源)。

  • *指针嵌入(`log.Logger)**:所有Job实例可共享同一个Logger实例,方法调用直接作用于原始对象;支持运行时动态重绑定(job.Logger = anotherLogger`),天然适配 Flyweight 模式,大幅提升内存效率与设计灵活性。

    AutoDraw
    AutoDraw

    AutoDraw是一个绘图工具,可以将草图转换成现成的模型图片

    下载

例如,考虑如下典型场景:

type Bitmap struct {
    data [4][5]bool
}

type Renderer struct {
    *Bitmap // 指针嵌入,支持共享底层数据
    on, off byte
}

func (r *Renderer) render() {
    for _, row := range r.data {
        for _, bit := range row {
            if bit {
                fmt.Print(string(r.on))
            } else {
                fmt.Print(string(r.off))
            }
        }
        fmt.Println()
    }
}

// 共享同一 Bitmap 实例
var pic Bitmap
pic.data[0][0] = true
pic.data[1][1] = true

renderA, renderB := Renderer{on: 'X', off: 'O'}, Renderer{on: '@', off: '.'}
renderA.Bitmap = &pic // 动态绑定
renderB.Bitmap = &pic // 同一底层数组

renderA.render() // 输出含 X/O 的图案
renderB.render() // 输出含 @/. 的同一图案(不同字符映射)

推荐实践

  • 优先使用 *T 指针嵌入,尤其当 T 包含同步原语(如 sync.Mutex)、大字段或需跨实例共享状态时;
  • 若 T 是小而无状态的纯数据结构(如 type Point struct{ X, Y int }),且明确需要隔离副本,可考虑值嵌入;
  • *永远不要嵌入 `interface{}或T`:它们不携带方法集,违背嵌入的设计初衷(方法提升);
  • 初始化时,配合 NewXXX() 工厂函数(返回 *T)自然契合指针嵌入,无需额外取地址操作。

总之,嵌入不是“语法糖”,而是有明确语义责任的设计选择。选择 *log.Logger 而非 log.Logger,不仅是惯用法,更是对可维护性、内存效率与并发安全的主动承诺。

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

314

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

527

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

191

2025.08.29

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

531

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

17

2025.12.22

漫蛙2入口地址合集
漫蛙2入口地址合集

本专题整合了漫蛙2入口汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号