spec: remove string(int)
В 2012 году Роб Пайк предложил убрать прямую конверсию int в строку:
string(123). Её добавили на заре языка, чтобы запустить форматированный вывод (если Робу "не изменяет память" 👍), и с тех пор она только путает. По сути, это просто закопанные грабли, от которых практически нет реальной пользы. В этом же issue он сам предложил устранить это в "ближайшем будущем".Ближайшее будущее наступило спустя 14 лет — 24 сентября этого года: пропозал приняли, правки спеки и тайпчекера уже в master. По сути, предупреждение, которое
go vet выдаёт с 2020 года, станет ошибкой компиляции — уже в Go v1.28На всякий случай напомню, в чём подвох:
fmt.Println(string(65)) // выведет "A", не "65"
fmt.Println(string(9786)) // вообще выведет "☺️"
Что конкретно изменится:
-
string(x) от целого компилируется только для byte, rune и нетипизированных rune-констант вроде 'a'-
int32 и uint8 тоже можно, т.к. это алиасы для rune и byte. Ещё разрешены именованные типы поверх них — type myByte byte.-
string(os.PathSeparator) выживает, т.к. это rune-константа. Ради таких случаев исключения и делали-
string(65), string(someInt) — ошибка компиляции- Для go.mod ниже 1.28 ничего не меняется, даже на новом тулчейне
Но самое весёлое, это история комментариев треда за 14 лет ✨
Вот краткая выжимка:
- 2013: Andrew Gerrand (adg) — нас держит обещание совместимости Go 1, ждём Go 2. Issue получает ярлык
go2 и про него забывают- 2016: человек ловит баг в проде на
string(x) от int16 и просит хотя бы vet-проверку. Роб Пайк: «It is valid code, so vet will not complain about it». Запомним 😐- 2019: Russ Cox понимает, что
"abc" + string(filepath.Separator) пишут все, и выпилить целиком не выйдет. Отсюда исключения для rune и byte- 2020: в Go 1.15 vet всё-таки начинает ругаться, причём прямо в
go test. В release notes это названо «первым пробным шагом к изменению языка». У Kubernetes и PingCAP после апгрейда падают тесты- Дальше issue снова откладывают: хотят посмотреть, как зайдёт vet-проверка, и дождаться, пока все перейдут на go.mod с версией языка
- Июль 2026: Griesemer от лица language review ставит likely decline 🗿. Потому что vet давно ловит, проблемного кода почти не осталось, выгоды мало
- Austin Clements: давайте привяжем к версии в go.mod, тогда старый код вообще не сломается. Да и ошибка компиляции лучше vet, потому что «Vet is not universal»
- Griesemer соглашается, через полтора месяца — accepted 🥂
Кстати, чинить пришлось и тесты самого Go. В древней папке
test/ken/ лежит файл с говорящим названием rob1.go, а в нём — string(this.val + '0') 🤷♀️С тулингом вышла история про курицу и яйцо. Модернизатор для
go fix обсуждают, но анализаторы не запускаются на коде с ошибками типов, а с go 1.28 в go.mod такой код уже падает с ошибкой. Поэтому либо надо будет прогнать go vet до того, как поднимать версию, либо будем ловить уже ошибки компиляции.Ну и в gopls уже смержили quick fix прямо на ошибку компиляции, он предлагает два варианта:
- "Convert single rune to string (preserves behavior)":
string(rune(x)), а для типов шире 32 бит fmt.Sprintf("%c", x);- "Format number as decimal (changes behavior)":
fmt.Sprintf("%d", x).————
🟢На практике вас это вряд ли заденет. Vet ловит такое в
go test с 2020 года, так что в живом коде этих конверсий почти не осталось.К слову, как видим, потихоньку начали разгребать то, что откладывали до Go 2, который так и не случился.
#go1_28 #proposal