Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni
🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!16.02.2023
Чому в Go є тип string та byte[]
Сьогоднішній баг стосувався того, що в Go у змінну з типом string
передавали ззовні значення nil
. (Там вхід з кодеком, що побудований на рефлексії, тож таке технічно можливо.) Виправилось це заміною типу на *string
, який може мати або значення nil
, або вказувати на рядок. Наскільки я знаю, доволі ідіоматичне рішення. Але воно наштовхнуло на роздуми — якщо в Go рядок це технічно слайс байтів, а слайси можуть бути nil, то чому рядки не можуть? Моя відповідь — напевно, щоб абстракція рядків була красивішою, тобто щоб рядки виглядали більше як “примітивний” тип.
Ось інші нюанси роботи з типами string та byte[]. Конверсія з рядка в слайс не безплатна, на відміну від інших. Операції string(someBytes)
та []byte(someString)
створюють копію даних. Це потрібно тому, що рядок — тип незмінний, тому не може розділяти памʼять зі змінним типом — слайсом. При цьому є багато API, які приймають тільки слайси або тільки рядки, тож деколи конверсії не уникнути. В мене був навіть випадок, коли рядкову функцію довелось переписати для слайсів, щоб прискорити вузьке місце. Ще, наприклад, функції форматування приймають обидва: можна написати fmt.Printf("contents of file are: %s", byteSlice)
.
Можна подумати, що рядки мають якусь семантику символів, тобто кодування. Але ні — в Go рядки складаються з байтів. Довжина вимірюється в байтах, позиції теж. Є один нюанс — цикли з range перебирають символи UTF-8. Це єдине місце, де я знаю, що рядки набувають кодування. Бо якщо рядок має некоректні символи, то такий цикл їх зіпсує. Або я майже впевнений, що деякі комбінації кодів він буде нормалізувати. Одним словом, краще бути обережним.