You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The conversion of an integer to a string was put in very early to bootstrap formatted
printing, if I remember correctly. It's odd, though, and no longer necessary, if it ever
was. It also causes inconsistencies, since string(0xD800) cannot produce the UTF-8
encoding for that code point (by definition, surrogates are not legal in UTF-8) so must
produce something else. We chose the "\uFFFD" since that's the only reasonable
option, but that means:
1) the result isn't obvious
2) string(0xD800) and "\uD800" do different things: the former produces the
UTF-8 for U+FFFD while the latter is statically rejected.
I propose that, in some remote future, we eliminate this conversion from the language.
@mateusz834Thanks for pointing this out - this is just a bug in the CL -int32 and rune as well as uint8 and byte should behave the same - they are aliases. Will fix.
Correction: I'm not sure why you say the examples don't work - they do work with the proposed changes (and they also work in 1.27). Anyway, I have added respective examples to the pending spec change and the implementation change.
Also, to be clear, when we talk about the type byte or rune in the spec, we always also mean uint8 and int32, respectively - aliases may have different names but denote the same type.
I have not checked the implementation, but only looked at the spec and thought that this case should be clarified better, also this discussion here did not mention the fact anywhere and thought that it might be worth bringing up so we are aware of this before this gets finally accepted. I got few times confused that rune and int32 are in fact the same type (e.g with reflect/generics), thus mentioned that, since i am aware that it is easy to miss that.
I have added respective examples to the pending spec change and the implementation change.
Thanks
Also, to be clear, when we talk about the type byte or rune in the spec, we always also mean uint8 and int32, respectively - aliases may have different names but denote the same type.
Oh I see, my initial thought was that we distinguish rune and int32 as two different things in this, since, say vet check could do that (i.e. require an explicit conversion to rune), but for the compiler it might not make sense.
@mateusz834 Ok, thanks for the explanations. Note that the spec is clear that aliases denote identical types (see the examples in Alias Declarations). Anyway, with the additional examples this should be clear now.
Out of curiosity: is this not the first breaking change (as in "code that would compile with Go 1.27 may no longer compiler with Go 1.28") ever introduced by a new version of Go (post 1.0)?
@jub0bs In a strict sense that is partly true (it's not the first, because we changed the semantics of loop iteration variables before). But go vet has checked exactly this for many years now, and the carve-outs in this proposal allow for exactly the cases where string(integer) still matters (like string(os.PathSeparator)). This has all been discussed in this issue.
I also note that the proposed new restriction only applies to Go 1.28 and newer. Thus, in the unlikely case that old code doesn't have a Go version and makes use of, say string(65), it will either need to be fixed (string(rune(65))) or have a Go module version. The latter is typically trivial; the former may be trivial, too.
No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— aclements for the proposal review group
The proposal is to restrict integer-to-string conversions to onlybyte or rune, starting in language version 1.28.
This restriction has long been implemented as a vet check. This proposal simply lifts it to the language level.
The detailed spec wording can be found in https://go.dev/cl/830044. The core change is simple (but could use some word-smithing):
A non-constant value <code>x</code> can be converted to type <code>T</code>
in any of these cases:
...
<li>
- <code>x</code> is an integer or a slice of bytes or runes- and <code>T</code> is a string type.+ <code>x</code> is an integer of type <code>byte</code> or <code>rune</code>+ or an untyped rune constant+ or a slice of bytes or runes and <code>T</code> is a string type.
</li>
...
<h4 id="Conversions_to_and_from_a_string_type">Conversions to and from a string type</h4>
...
-Finally, for historical reasons, an integer value may be converted to a string type.+Finally, an integer value of type <code>byte</code> or <code>rune</code>+or an untyped rune constant+may be converted to a string type.
Activity
adg commentedon Jan 21, 2013
Comment 1:
rsc commentedon Jul 30, 2013
Comment 2:
Labels changed: added go2.
rsc commentedon Dec 4, 2013
Comment 3:
Labels changed: added repo-main.
rsc commentedon Mar 3, 2014
Comment 4:
Labels changed: added release-none.
74 remaining items
griesemer commentedon Sep 17, 2026
@mateusz834
Thanks for pointing this out - this is just a bug in the CL -int32andruneas well asuint8andbyteshould behave the same- they are aliases. Will fix.Correction: I'm not sure why you say the examples don't work - they do work with the proposed changes (and they also work in 1.27). Anyway, I have added respective examples to the pending spec change and the implementation change.
Also, to be clear, when we talk about the type
byteorrunein the spec, we always also meanuint8andint32, respectively - aliases may have different names but denote the same type.mateusz834 commentedon Sep 17, 2026
I have not checked the implementation, but only looked at the spec and thought that this case should be clarified better, also this discussion here did not mention the fact anywhere and thought that it might be worth bringing up so we are aware of this before this gets finally accepted. I got few times confused that
runeandint32are in fact the same type (e.g with reflect/generics), thus mentioned that, since i am aware that it is easy to miss that.Thanks
Oh I see, my initial thought was that we distinguish
runeandint32as two different things in this, since, say vet check could do that (i.e. require an explicit conversion to rune), but for the compiler it might not make sense.griesemer commentedon Sep 17, 2026
@mateusz834 Ok, thanks for the explanations. Note that the spec is clear that aliases denote identical types (see the examples in Alias Declarations). Anyway, with the additional examples this should be clear now.
go/types, types2: implement new string(x) conversion restrictions
jub0bs commentedon Sep 23, 2026
Out of curiosity: is this not the first breaking change (as in "code that would compile with Go 1.27 may no longer compiler with Go 1.28") ever introduced by a new version of Go (post 1.0)?
griesemer commentedon Sep 23, 2026
@jub0bs In a strict sense that is partly true (it's not the first, because we changed the semantics of loop iteration variables before). But go vet has checked exactly this for many years now, and the carve-outs in this proposal allow for exactly the cases where string(integer) still matters (like
string(os.PathSeparator)). This has all been discussed in this issue.I also note that the proposed new restriction only applies to Go 1.28 and newer. Thus, in the unlikely case that old code doesn't have a Go version and makes use of, say
string(65), it will either need to be fixed (string(rune(65))) or have a Go module version. The latter is typically trivial; the former may be trivial, too.zigo101 commentedon Sep 24, 2026
aclements commentedon Sep 24, 2026
No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— aclements for the proposal review group
The proposal is to restrict integer-to-string conversions to only
byteorrune, starting in language version 1.28.This restriction has long been implemented as a vet check. This proposal simply lifts it to the language level.
The detailed spec wording can be found in https://go.dev/cl/830044. The core change is simple (but could use some word-smithing):
[-]proposal: spec: remove string(int)[/-][+]spec: remove string(int)[/+]spec: string(x) for integers requires x to be of (underlying) type by…
rothelius commentedon Sep 25, 2026
How will named types with
byte/uint8orrune/int32as underlying type work?This works today:
Will
string(myByte('A'))work, or will you have to dostring(byte(myByte('A')))ianlancetaylor commentedon Sep 25, 2026
@rothelius Conversions from a named
uint8orint32type tostringshould continue to work as they do today.This is clear in the spec change https://go.dev/cl/830044.
rothelius commentedon Sep 25, 2026
Ah, I see it's been amended. Yes, much clearer now 😃