The implementation is
// String returns the accumulated string.
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
According to my test, converting []byte to string uses "copy on write", or the compiler generates the deep copy instructions if either one is changing the insided slice:
{
a := []byte{'a'}
s1 := string(a)
a[0] = 'b'
fmt.Println(s1) // a
}
{
a := "a"
b := []byte(a)
b[0] = 'b'
fmt.Println(a) // a
}
So what happens if it's implemented as below?
// String returns the accumulated string.
func (b *Builder) String() string {
return string(b.buf)
}
You can view the discussion on the changelist that introduced the strings.Builder
api here: https://go-review.googlesource.com/c/go/+/74931/4/src/strings/builder.go#30
As you'd expect, it's a discussion of API mechanics, correctness, and efficiency.
If you replace the code with string(b.buf)
, you'll cause a copy of the built string. It may be that the compiler optimizes away the copy in simple cases of converting a byte slice to a string, but it's very unlikely that the compiler can do that here in general (because it would require a proof that the buffer inside the string builder is never used again).
Note that the (standard-library) code looks dangerous, because if you write this:
var b strings.Builder
b.WriteString("hello world")
c := b.String()
b.WriteString("a")
d := b.String()
then c
and d
will end up pointing to the same memory. But that's fine, because strings contain the length of their buffer. And there's no way to mutate a string, because even though in theory the memory backing the string is accessible via the buf
in the strings.Builder
, the only apis provided append to the backed memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With