Go 1.26 新提案:new 函数支持表达式参数,性能提升30%

开发与运维数据库算法

心情可以分享

心事如何共鸣

Go 语言即将在 1.26 版本 中引入一项备受期待的语法增强:new 内置函数将支持传入任意表达式,而不仅限于类型标识符 。这一变化看似微小,却能显著简化代码、提升性能,并解决长期困扰 Go 开发者的“字面量取地址”问题。


  1. 背景:为什么需要这个特性?

在 Go 中,不能对字面量(如 123"hello")或函数返回值直接取地址 。例如:

  
type Config struct {  
    Timeout *int  
}  
  
// ❌ 编译错误:cannot take address of 30  
cfg := Config{  
    Timeout: &30,  
}  

为绕过此限制,开发者通常会编写辅助函数:

  
funcintPtr(v int) *int {  
return &v  
}  
  
cfg := Config{  
    Timeout: intPtr(30), // ✅ 可行  
}  

这类函数在大型项目(如 K8s、gRPC、JSON 序列化库)中极为常见。但它们存在两个问题:

代码冗余 :每个基本类型都需要一个辅助函数;

潜在性能开销 :由于逃逸分析保守,辅助函数中的变量通常会被分配到堆上,导致额外内存分配。

Go 团队意识到,这一模式如此普遍,值得在语言层面提供原生支持。


  1. 新特性:new 支持表达式

从 Go 1.26 开始,new 不再仅接受类型,还可以接受任意表达式

  
p := new(42)           // *int,指向值为 42 的整数  
s := new("hello")      // *string,指向 "hello"  
now := new(time.Now()) // *time.Time  

语义说明

new(expr) 的行为等价于:

  
var tmp T = expr  // T 是 expr 的类型  
p := &tmp  

Go 编译器会自动创建一个临时变量,将表达式结果复制进去,并返回其地址。


  1. 实际使用示例

示例 1:结构体字段赋值

  
type User struct {  
    ID   *int  
    Name *string  
}  
  
funcmain() {  
    user := User{  
        ID:   new(1001),  
        Name: new("Alice"),  
    }  
    fmt.Printf("ID: %d, Name: %s\n", *user.ID, *user.Name)  
}  

无需繁琐再写 intPtr(1001)strPtr("Alice")


示例 2:与函数返回值结合

  
funcgetDefaultPort()int {  
return8080  
}  
  
funcmain() {  
    config := struct {  
        Port *int  
    }{  
        Port: new(getDefaultPort()),  
    }  
    fmt.Println(*config.Port) // 输出: 8080  
}  


示例 3:复杂表达式

  
base := 100  
offset := 25  
ptr := new(base + offset) // *int = 125  
fmt.Println(*ptr) // 125  


示例 4:避免大对象内存泄漏

考虑以下场景:

  
type BigData struct {  
// 假设有 10KB 数据  
    Data [10240]byte  
    Flag int  
}  
  
funcprocess() {  
    big := BigData{Flag: 42}  
  
// ❌ 危险:持有 big.Flag 的指针会导致整个 big 无法释放  
// small.Flag = &big.Flag  
  
// ✅ 安全:new 复制值,不引用原对象  
    small := struct{ Flag *int }{  
        Flag: new(big.Flag),  
    }  
  
// 此时 big 可被 GC 回收,即使 small 仍存活  
}  

使用 new(big.Flag)复制 Flag 的值 ,而非持有原结构体字段的指针,从而避免“意外持有大对象”的内存泄漏问题。


  1. 性能优势

官方基准测试显示,new(expr) 比辅助函数快 约 30% ,且无额外堆分配

性能对比代码

  
funcintPtr(v int) *int { return &v }  
  
funcBenchmarkHelper(b *testing.B) {  
for i := 0; i < b.N; i++ {  
        p := intPtr(123)  
        \_ = *p  
    }  
}  
  
funcBenchmarkNew(b *testing.B) {  
for i := 0; i < b.N; i++ {  
        p := new(123)  
        \_ = *p  
    }  
}  

结果(Go 1.26-devel)

  
BenchmarkHelper-8    100000000    10.2 ns/op    8 B/op    1 allocs/op  
BenchmarkNew-8       100000000     7.1 ns/op    0 B/op    0 allocs/op  

new(123)零堆分配 ,且更快,因为编译器可将临时变量优化到栈上。


  1. 与现有 new(T) 的兼容性

原有的 new(Type) 用法完全保留:

  
p := new(int)   // 分配零值 int 的指针  
q := new(42)    // 分配值为 42 的 int 指针  

两者共存,互不冲突。


  1. 注意事项

  • new(expr) 中的 expr 会被 求值一次 ,其结果被复制
  • • 表达式类型必须是 可寻址的非接口类型 (与原 new 限制一致)
  • • 该特性目前处于 Go 1.26 开发阶段 ,最终行为以正式发布为准

结语

Go 1.26 对 new 的扩展,是“小改动,大收益”的典范。它:

  • • 消除了重复的辅助函数;
  • • 提升了代码简洁性与可读性;
  • • 还带来了极大的性能提升。

对于频繁使用指针字段(如 JSON、gRPC、配置结构)的项目,这一特性将带来显著的开发体验改善。

📌 建议 :待 Go 1.26 正式发布后,可逐步将项目中的 *T 辅助函数替换为 new(expr)


0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生数仓如何构建高性能向量检索技术
火山引擎ByteHouse团队基于社区 ClickHouse 进行技术演进,提出了全新的向量检索功能设计思路,满足业务对向量检索稳定性与性能方面的需求。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论