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.
