Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

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. Це єдине місце, де я знаю, що рядки набувають кодування. Бо якщо рядок має некоректні символи, то такий цикл їх зіпсує. Або я майже впевнений, що деякі комбінації кодів він буде нормалізувати. Одним словом, краще бути обережним.