Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spec: remove string(int/rune) #3939

Open
robpike opened this Issue Aug 10, 2012 · 25 comments

Comments

Projects
None yet
@robpike
Copy link
Contributor

robpike commented Aug 10, 2012

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

This comment has been minimized.

Copy link
Contributor

adg commented Jan 21, 2013

Comment 1:

I guess we're bound by the Go 1 compatibility promise to keep this until Go 2.
@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Jul 30, 2013

Comment 2:

Labels changed: added go2.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Dec 4, 2013

Comment 3:

Labels changed: added repo-main.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 3, 2014

Comment 4:

Adding Release=None to all Priority=Someday bugs.

Labels changed: added release-none.

@robpike robpike self-assigned this Mar 3, 2014

@rsc rsc added this to the Unplanned milestone Apr 10, 2015

@robpike robpike removed their assignment Sep 21, 2015

@dolmen

This comment has been minimized.

Copy link

dolmen commented Dec 14, 2016

I've been beaten by this "feature" in production code today.
My code was equivalent to this (but of course not so obvious: type, const and cast were in 3 different packages, and the cast result was an argument to a function, so that made the bad use of the cast quite hidden):

   type XType int16
   const x XType = 0x0001
   var y = string(x)

I think that go vet should raise an alert at least for my case (conversion to string of a value of a named integer type).

@robpike

This comment has been minimized.

Copy link
Contributor Author

robpike commented Dec 15, 2016

It is valid code, so vet will not complain about it. Perhaps golint should, but not vet.

@rsc rsc changed the title spec: disallow string(int/rune) spec: remove string(int/rune) Jun 17, 2017

@rsc rsc changed the title spec: remove string(int/rune) proposal: spec: remove string(int/rune) Jun 17, 2017

@urandom

This comment has been minimized.

Copy link

urandom commented Oct 25, 2017

Would it be possible to make string(int), return the string representation of the int. More users will probably expect this behaviour:

a := 42
fmt.Print(string(a)) // "42"

EDIT: got it, bad idea

@dominikh

This comment has been minimized.

Copy link
Member

dominikh commented Oct 25, 2017

@urandom I would not expect an innocuously looking conversion to invoke an expensive number formatting routine. The solution to fixing a (supposedly) confusing construct isn't to replace it with another, equally confusing one.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Oct 25, 2017

No, that is not possible. It will silently break existing programs in a subtle way, for no real reason. If you want to convert an int (or a float or a bool) to a string, use package strconv. In particular, using strconv allows you more control over the exact formatting.

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Jun 27, 2018

Another argument in favor of abandoning this feature is that it behaves differently when shifts are involved. See #26096 .

Jay54520 added a commit to Jay54520/Learn-Algorithms-With-Go that referenced this issue Jul 3, 2018

012-打印1到最大的N位数: v4: feat: add PermutationNum
使用 strconv.Itoa(num) 而不是 string(num) 转换数字

在 golang/go#3939 提议移除 string(int/rune),因为它不能提供正确的 UTF-8 编码点
@kindlyfire

This comment has been minimized.

Copy link

kindlyfire commented Aug 12, 2018

I wonder how to achieve what string(int) does with strconv ?

Doing string(65) returns "A", but I can't find a function in strconv that does that.

@cznic

This comment has been minimized.

Copy link
Contributor

cznic commented Aug 12, 2018

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Aug 13, 2018

string(int) is really quite a complex operation as it needs to know utf8 encoding. @robpike 's original comment in this issue explains the rationale for removing it. Now that we have the proper libraries (uniccode, utf8) we can write this code explicitly. See e.g. runeToString here: https://play.golang.org/p/ZnUF0Oc_dAG .

@magical

This comment has been minimized.

Copy link
Contributor

magical commented Sep 13, 2018

@griesemer You don't even have to use a library. This works: string([]rune{65}) https://play.golang.org/p/I6aX3c6fG71. Unless we're proposing removing string([]rune) as well.

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Sep 13, 2018

@magical Good point! I don't know about removing string([]rune). String(int/rune) is particularly annoying because the argument may be shift operation which opens a can of worms (see #26096 and various related issues).

@mimoo

This comment has been minimized.

Copy link

mimoo commented Oct 26, 2018

Note that python allows you to do str(42) -> "42" so this might bring confusion also.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Feb 13, 2019

I noticed that some existing code uses string(os.PathSeparator), which would become invalid under this proposal. Not a reason not to do it, just a note about consequences.

@networkimprov

This comment has been minimized.

Copy link

networkimprov commented Mar 6, 2019

This would break code that uses character constants as switches/flags/arguments, which
a) can be included in a string (e.g. filename, URL), and
b) are meaningful when printed in a log.

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Mar 6, 2019

@networkimprov I'm not sure I follow your comment. Can you be a bit more specific (example)?

One place where string(x) cannot simply be replaced with a corresponding library call is where x is a constant and we rely on the fact that string(x) also is a constant:

const s = string(c)

This won't be possible anymore. In many cases, const can be replaced with var; in others it may be necessary to do the string(c) manually rather than have it done by the compiler.

@networkimprov

This comment has been minimized.

Copy link

networkimprov commented Mar 6, 2019

I use string(char) like this:

const ( eThis = 'I'; eThat = 'A' ) // alternative to iota
...
aFileName := aDir +"/"+ string(eThis) + aName
...
func F(iFlag byte, iData T) {
   log.Println("F with ", string(iFlag))

   switch iFlag {
   case eThis: ...
   case eThat: ...
   }
}
@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 6, 2019

In all the discussion of this proposal I'd been focusing in my head on

i := 3
s := string(i)

which is clearly wrong and not going to hurt to remove. But I hadn't thought much about

c := 'x'
s := string(c)

which is clearly fine and does happen and will hurt more to remove. Worse, it happens in constants like "abc"+string(filepath.Separator).

#30614 proposes adding filepath.SeparatorString, but having to update all that code seems unfortunate.

We could make this issue about rejecting string(int) but not string(rune), but today string(rune) and string(int32) are indistinguishable, so string(int32) would slip in too.

Or perhaps we should put this issue (#3939) off until we decide #29012 (separate rune and int32).

@go101

This comment has been minimized.

Copy link

go101 commented Mar 6, 2019

Is it ok to add a builtin char function to return one-rune string?

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Mar 6, 2019

I suppose one could add a new built-in, but char seems an unfortunate name for a function returning a string.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Mar 6, 2019

I don't see any meaningful difference between a char builtin and the current string conversion.

@go101

This comment has been minimized.

Copy link

go101 commented Mar 6, 2019

It has the benefit of avoid mistaking stirng(aInt) as the string representation of aInt, but not more others.

How about allow concentrating constant strings and runes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.