Это не столько проблема компилятора, сколько проблема спецификации языка. Компилятор может и иногда будет делать странные вещи - здесь важно то, что какой бы машинный код в конечном итоге не выплюнул, он следует правилам, изложенным в спецификации языка.
Как упоминалось в комментариях, спецификация языка определяет преобразование byte
фрагментов в и из string
типов, например:
Преобразование фрагмента байтов в строковый тип дает строку, чьи последовательные байты являются элементами среза.
Преобразование значения строкового типа в срез байтового типа дает срез, чьи последовательные элементы являются байтами строки.
Чтобы понять поведение ваших примеров, вы должны также прочитать определение типов string
, также в спецификации :
Строки неизменяемы: после создания невозможно изменить содержимое строки.
Поскольку []byte
является изменяемым, за кулисами go должен сделать копию соответствующих данных при преобразовании в string
и обратно. Это можно проверить, напечатав адреса 0-го элемента объекта []byte
и указателя на первый элемент данных в объекте string
. Вот пример (и Go Версия игровой площадки ):
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "a"
b := []byte(a)
ah := (*reflect.StringHeader)(unsafe.Pointer(&a))
fmt.Printf("a: %4s @ %#x\n", a, ah.Data)
fmt.Printf("b: %v @ %p\n\n", b, b)
c := []byte{'a'}
d := string(c)
dh := (*reflect.StringHeader)(unsafe.Pointer(&d))
fmt.Printf("c: %v @ %p\n", c, c)
fmt.Printf("d: %4s @ %#x\n", d, dh.Data)
}
Результат выглядит следующим образом:
a: a @ 0x4c1ab2
b: [97] @ 0xc00002c008
c: [97] @ 0xc00002c060
d: a @ 0x554e21
Обратите внимание, что расположение указателя string
и []byte
не совпадают и не перекрываются. Поэтому нет никаких ожиданий, что изменение значений []byte
каким-либо образом повлияет на значения string
.
Хорошо, технически результат не должен был быть таким образом, потому что в моем примере я не вносил никаких изменений в значения b
или c
. Технически компилятор мог использовать ярлык и просто вызвать b
a length = 1 []byte
, начиная с того же адреса памяти, что и a
. Но эта оптимизация была бы невозможна, если бы я сделал что-то вроде этого:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "a"
b := []byte(a)
b[0] = 'b'
ah := (*reflect.StringHeader)(unsafe.Pointer(&a))
fmt.Printf("a: %4s @ %#x\n", a, ah.Data)
fmt.Printf("b: %v @ %p\n\n", b, b)
}
Вывод:
a: a @ 0x4c1ab2
b: [98] @ 0xc00002c008
Посмотрите это в действии на Go Playground .