如何在 Go 中克隆 strings.Builder?

Dan*_*ray 3 go

Go 编程语言的标准库公开了一个名为 的结构strings.Builder,它允许通过重复连接以有效的方式轻松构建字符串,类似于 C# 或 Java 的StringBuilder.

在 Java 中,我会使用StringBuilder的构造函数来“克隆”对象,如下所示:

StringBuilder newBuffer = new StringBuilder(oldBuffer.toString());
Run Code Online (Sandbox Code Playgroud)

在Go中,我只能看到以下两行方式:

newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)

没有其他.Clone()初始化方法(我可能还没有找到)。

是否有另一种方法比我提出的方法更简短/简洁?

Hym*_*sco 5

出于好奇而进入不必要的细节......

考虑完文档后,以下是您的主要问题:

  1. 从 Builder 中读取数据的唯一导出方式是Builder.String方法。
  2. Builder一旦操作了值,复制它就不安全了。

我们来看看这个版本:

newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)

我首先想到为什么这是不可取的,因为Builder内部使用字节切片(可变数据类型),并返回字符串(不可变数据类型)。尽管字符串的底层表示与字节切片相同,但由于这种可变性规则,它需要一个副本才能转换为字符串。这意味着当您将字符串写入新缓冲区时,您已经在第二个副本上,而您的任务直观上只需要一个副本。

然而,实际上看一下源代码,我们会发现这个假设是错误的:

func (b *Builder) String() string {
    return *(*string)(unsafe.Pointer(&b.buf))
}
Run Code Online (Sandbox Code Playgroud)

使用该unsafe包,该strings包基本上将缓冲区([]byte)直接“破解”为string. 同样,这些数据类型在内存级别上是相同的:指向字符串或切片开头的指针,以及描述字符串或切片长度的指针偏移量。这些数据类型只是标头,因此此处没有发生缓冲区复制。

这会造成一种不舒服的情况,即您有一个应该是不可变的字符串,但您仍然在某个地方有一个字节切片,可能会改变这些底层字节。包unsafe毕竟被调用了,这是一个很好的例子来说明为什么会这样。

因为它strings.Builder纯粹是一个“构建器”,即它只能创建字符串的新部分,并且永远不会修改已写入的数据,所以我们仍然获得语言“保证”的字符串的不变性。我们打破该规则的唯一方法是访问 的内部bufBuilder但由于该字段未导出,您将再次需要unsafe自己去访问它。

概括:

您想出的简单方法,虽然可能比预期的长一行(或两行),但它是确定且正确的方法。即使您推出了 Go 的更强大的功能(如unsafe和 ) ,它也已经和您将获得的一样高效reflect

我希望这能提供一些信息。以下是对代码的唯一建议更改:

// clone the builder contents. this is fast.
newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)