Home > Software design >  Why Golang string.Builder String() can convert []byte to slice without a reflect.StringHeader?
Why Golang string.Builder String() can convert []byte to slice without a reflect.StringHeader?

Time:01-10

Go's strings package defines a Builder type which has a String() method

func (b *Builder) String() string {
    return *(*string)(unsafe.Pointer(&b.buf))
}

as reflect pacakge indicates, a byte slice is defined as a SliceHeader and an associated data block pointed by the header's Data field:

type SliceHeader struct {
  Data uintptr
  Len  int
  Cap  int
}

and string is defined as a StringHeader and an associated data block pointed by the header's Data field

type StringHeader struct {
  Data uintptr
  Len  int
}

the SliceHeader has 3 fields(Data, Len and Cap) and StringHeader has only 2(Data and Len), so how can one convert byte slice to string directly like this ?

*(*string)(unsafe.Pointer(&buf))

in my understanding, we should code following:

func byte2str(b []byte) string {
  hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
  return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: hdr.Data, Len: hdr.Len}))
}

CodePudding user response:

Let's break it down.

The expression &buf is a pointer to the slice header. A pointer to a slice is a pointer to the slice header.

The expression (unsafe.Pointer(&buf) is a conversion from the slice header pointer to an unsafe.Pointer. The unsafe.Pointer type has a magical property — it can be converted to and from any other pointer type.

The expression is (*string)(unsafe.Pointer(&buf)) is a conversion from a slice header pointer to a string header pointer. A pointer to a string is a pointer to the header.

At this point, we have a string header pointer that's actually pointing to a slice header. That might seem bad at first glance, but all is good. The memory layout of a string header is a prefix of the memory layout for a slice header.

The expression *(*string)(unsafe.Pointer(&buf)) dereferences the string header pointer to get the string. This operation copies data and length fields from the slice header to a string header.

This code will break if the slice header fields are reordered without a corresponding change to the string header fields, but that's never going to happen.

  •  Tags:  
  • Related