Skip to content

spec: remove string(int) #3939

Open
Open

Description

@robpike
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.

Activity

  1. adg commented on Jan 21, 2013

    @adg
    Contributor

    Comment 1:

    I guess we're bound by the Go 1 compatibility promise to keep this until Go 2.
  2. rsc commented on Jul 30, 2013

    @rsc
    Contributor

    Comment 2:

    Labels changed: added go2.

  3. rsc commented on Dec 4, 2013

    @rsc
    Contributor

    Comment 3:

    Labels changed: added repo-main.

  4. rsc commented on Mar 3, 2014

    @rsc
    Contributor

    Comment 4:

    Adding Release=None to all Priority=Someday bugs.

    Labels changed: added release-none.

  5. self-assigned this
    on Mar 3, 2014
  6. added this to the Unplanned milestone on Apr 10, 2015
  7. 74 remaining items

  8. griesemer commented on Sep 17, 2026

    @griesemer
    Contributor

    @mateusz834 Thanks 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.

  9. mateusz834 commented on Sep 17, 2026

    @mateusz834
    Member

    #3939 (comment) don't work

    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.

  10. griesemer commented on Sep 17, 2026

    @griesemer
    Contributor

    @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.

  11. jub0bs commented on Sep 23, 2026

    @jub0bs
    Contributor

    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)?

  12. griesemer commented on Sep 23, 2026

    @griesemer
    Contributor

    @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.

  13. zigo101 commented on Sep 24, 2026

    @zigo101
  14. aclements commented on Sep 24, 2026

    @aclements
    Member

    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 byte 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.
  15. changed the title [-]proposal: spec: remove string(int)[/-] [+]spec: remove string(int)[/+] on Sep 24, 2026
  16. rothelius commented on Sep 25, 2026

    @rothelius

    How will named types with byte/uint8 or rune/int32 as underlying type work?

    This works today:

    type myByte byte
    
    _ = string([]myByte{'h', 'e', 'l', 'l', 'o'})

    Will string(myByte('A')) work, or will you have to do string(byte(myByte('A')))

  17. ianlancetaylor commented on Sep 25, 2026

    @ianlancetaylor
    Contributor

    @rothelius Conversions from a named uint8 or int32 type to string should continue to work as they do today.

    This is clear in the spec change https://go.dev/cl/830044.

  18. rothelius commented on Sep 25, 2026

    @rothelius

    @rothelius Conversions from a named uint8 or int32 type to string should continue to work as they do today.

    This is clear in the spec change https://go.dev/cl/830044.

    Ah, I see it's been amended. Yes, much clearer now 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

  • Status
    Accepted

Relationships

None yet

Development

No branches or pull requests

Issue actions