首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Go 1.26 新特性预览:扩展 new() 函数支持表达式参数

Go 1.26 新特性预览:扩展 new() 函数支持表达式参数

作者头像
编码如写诗
发布2026-03-02 20:43:17
发布2026-03-02 20:43:17
390
举报
文章被收录于专栏:编码如写诗编码如写诗

在 Go 语言中,创建复合类型(如结构体)的指针非常方便,可以直接使用 &S{a: 3} 语法。但创建简单类型(如 int、string)的指针却需要多步操作:先声明变量,再取地址。这种不一致性长期困扰着开发者。Go 1.26 将通过扩展 new() 内置函数来解决这个问题,使简单类型和复合类型的指针创建方式更加统一。

背景与问题

目前的情况:

在 Go 语言中,创建指针的方式存在不一致性:

  • 复合类型(结构体、数组、map 等):可以直接使用 & 操作符创建指针
  • 简单类型(int、string、bool 等):必须先声明变量,再取地址

存在的问题:

代码语言:javascript
复制
// 复合类型 - 非常方便
p1 := &User{Name: "Alice"}  // 直接创建结构体指针
p2 := &[3]int{1, 2, 3}    // 直接创建数组指针

// 简单类型 - 需要多步操作
a := 3
p3 := &a  // 必须先声明变量

// 或者使用 new() 函数
p4 := new(int)      // 只能创建零值指针
*p4 = 3            // 然后赋值

这种不一致性导致:

  1. 代码冗长,不够简洁
  2. 复合类型和简单类型的处理方式不统一
  3. 在某些场景下无法直接获取表达式的指针(如函数返回值)

核心概念

复合类型 (Composite Types)

Go 中的复合类型包括:

  • 结构体(struct)
  • 数组(array)
  • 切片(slice)
  • 映射(map)

这些类型可以直接使用字面量语法创建,并用 & 操作符获取指针。

简单类型 (Simple Types)

简单类型包括:

  • 基本类型:int、float、string、bool
  • 指针类型
  • 函数类型
  • 接口类型

这些类型不能直接取地址,必须通过变量或 new() 函数创建指针。

new() 内置函数

当前 Go 的 new() 函数:

  • 接受一个类型参数 T
  • 返回指向类型 T 零值的指针 *T
  • 示例:new(int) 返回指向 int 零值(0)的指针

提案扩展

Go 1.26 提案将扩展 new() 函数:

  • 新增可选的表达式参数
  • 语法:new(T, expression)
  • 行为:创建类型 T 的指针,将 expression 的值赋给该指针指向的内存

代码示例详解

示例 1: 创建基本类型指针

代码语言:javascript
复制
// 当前写法 (Go 1.25 及之前)
a := 3
p1 := &a  // 需要先声明变量

// 新写法 (Go 1.26)
p2 := new(int, 3)           // 直接创建指向 3 的指针
fmt.Println(*p2)             // 输出: 3

// 同样适用于字符串
s := new(string, "hello")    // 直接创建指向 "hello" 的指针
fmt.Println(*s)             // 输出: hello

代码讲解:新语法 new(int, 3) 创建了一个 int 类型的指针,并立即将值 3 赋给它。这相当于:

代码语言:javascript
复制
temp := 3
p := &temp

但编译器会优化这一过程,直接在栈上分配内存。

示例 2: 函数返回值的指针

代码语言:javascript
复制
func getValue() int {
    return42
}

// 当前写法 - 无法直接获取
func getValuePointer1() *int {
    v := getValue()
    return &v  // 需要中间变量
}

// 新写法 (Go 1.26)
func getValuePointer2() *int {
    returnnew(int, getValue())  // 直接获取返回值的指针
}

代码讲解:新语法允许直接获取表达式的指针,这对于获取函数返回值、复杂计算结果的指针非常有用。

示例 3: 处理 JSON/gRPC 数据

代码语言:javascript
复制
// 假设有一个 API 响应结构体
type Response struct {
    ID   int    `json:"id"`
    Name  string`json:"name"`
    Score *int   `json:"score,omitempty"`
}

// 当前写法
func processResponseOld(resp *Response) {
    score := 100
    if resp.Name != "" {
        resp.Score = &score  // 需要中间变量
    }
}

// 新写法 (Go 1.26)
func processResponseNew(resp *Response) {
    if resp.Name != "" {
        resp.Score = new(int, 100)  // 直接创建指针
    }
}

代码讲解:在处理 JSON、gRPC 等协议时,经常需要为可选字段创建指针。新语法使代码更加简洁。

实现原理/限制分析

实现原理:

编译器处理

  • 当遇到 new(T, expr) 时,编译器会生成类似以下代码:var temp T = expr return &temp

内存分配优化

  • 编译器会进行逃逸分析(escape analysis)
  • 如果指针不会逃逸到函数外,会在栈上分配内存
  • 性能与手动方式 v := expr; &v 相同

类型检查

  • 确保表达式 expr 的类型可以赋值给类型 T
  • 编译时进行类型安全检查

限制和边界情况:

类型必须匹配

代码语言:javascript
复制
// 编译错误
p := new(int, "hello")  // string 不能赋值给 int

不能获取栈上临时变量的地址

代码语言:javascript
复制
// 新语法解决的问题
p := new(int, someFunction() + 5)  // ✅ 允许

与现有 new() 语法共存

代码语言:javascript
复制
p1 := new(int)      // ✅ 旧语法仍然有效
p2 := new(int, 42)  // ✅ 新语法

不能用于复合类型

代码语言:javascript
复制
// 复合类型仍然使用 & 操作符
p := &User{Name: "Alice"}  // ✅ 推荐方式
p := new(User, User{Name: "Alice"})  // ❌ 不推荐

与其他语言/框架对比

特性

Go 1.26

Go 1.25

Java

Python

基本类型指针创建

new(int, 42)

需中间变量

无指针概念

无指针概念

复合类型指针创建

&User{}

&User{}

对象引用

对象引用

类型安全性

编译时检查

编译时检查

编译时检查

运行时检查

内存分配位置

栈/堆自动优化

栈/堆自动优化

学习曲线

对比分析:

  • Go vs Java: Java 没有指针概念,所有对象都是引用,但基本类型(int 等)不是引用,需要包装类(如 Integer)。Go 的新语法在保持类型安全的同时,提供了更直接的指针操作方式。
  • Go vs C/C++: C/C++ 允许对任意表达式取地址,但容易导致悬空指针。Go 的限制确保了内存安全。

最佳实践与注意事项

✅ 推荐做法:

对于基本类型,使用新的 new() 语法

代码语言:javascript
复制
p := new(int, 42)
s := new(string, "hello")

对于复合类型,继续使用 & 操作符

代码语言:javascript
复制
p := &User{Name: "Alice"}

利用编译器的逃逸分析

代码语言:javascript
复制
// 函数内使用,不会逃逸到堆
func process() {
    p := new(int, calculate())
    // 使用 p
}

⚠️ 注意事项:

类型匹配必须精确

代码语言:javascript
复制
// 可能的类型转换问题
var x int32 = 42
p := new(int, x)  // ⚠️ int32 到 int 的隐式转换

注意 nil 指针问题

代码语言:javascript
复制
// 新语法创建的指针永远不为 nil
p := new(int, 42)
if p == nil {  // 永远不会执行
}

性能考虑

代码语言:javascript
复制
// 在热路径中,避免频繁创建指针
for i := 0; i < 1000000; i++ {
    p := new(int, i)  // ⚠️ 可能影响性能
}

❌ 避免做法:

不要对复合类型使用 new(expr)

代码语言:javascript
复制
// 不推荐
p := new(User, User{Name: "Alice"})
// 推荐使用
p := &User{Name: "Alice"}

不要过度使用指针

代码语言:javascript
复制
// 不必要的指针
p := new(int, 42)
fmt.Println(*p)  // 直接使用 42 更好

个人观点

我的看法:

这个提案是一个非常实用和务实的改进。它解决了 Go 语言中一个长期存在的不一致性问题,使简单类型和复合类型的处理方式更加统一。

主要优点:

  1. 一致性: 消除了复合类型和简单类型之间的不一致性
  2. 简洁性: 减少了模板代码,提高了代码可读性
  3. 向后兼容: 完全兼容现有代码,不影响老项目
  4. 实用性: 在处理 JSON、gRPC 等协议时特别有用

潜在问题:

  1. 增加语法复杂度: new() 函数有两种不同的用法可能会让初学者困惑
  2. 滥用风险: 可能导致不必要的指针使用

适用场景:

  1. 处理 JSON/gRPC 响应: 创建可选字段的指针
  2. 函数返回值: 直接获取函数返回结果的指针
  3. 复杂表达式: 获取计算结果的指针,无需中间变量

未来展望:

这个提案将在 Go 1.26 中实现。建议开发者:

  1. 在 Go 1.26 发布后,逐步在新代码中使用新语法
  2. 现有代码不需要立即修改,保持兼容性
  3. 关注官方文档和 Go Blog 了解更多使用示例

参考链接:

  • 原文链接: Go 官方 GitHub - spec: expression to create pointer to simple types[1]

引用链接

[1]

Github: https://github.com/golang/go/issues/45624

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编码如写诗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景与问题
  • 核心概念
  • 代码示例详解
    • 示例 1: 创建基本类型指针
    • 示例 2: 函数返回值的指针
    • 示例 3: 处理 JSON/gRPC 数据
  • 实现原理/限制分析
  • 与其他语言/框架对比
  • 最佳实践与注意事项
  • 个人观点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档