Why isn't there strings.reverse("str") function?

1,953 views
Skip to first unread message

Amarjeet Anand

unread,
Feb 15, 2020, 11:37:15 AM2/15/20
to golang-nuts
Hi

I was wondering why isn't there built-in string reverse function. Is it left intentionally because of some reason?

Although strings are immutable in go, there are multiple ways to achieve this pretty easily. But having this function inbuilt will save our time because we need it quite often.


Ian Lance Taylor

unread,
Feb 15, 2020, 11:45:03 AM2/15/20
to Amarjeet Anand, golang-nuts
It's been suggested, but it doesn't seem to come up that much, and
it's easy to write your own version. Also, other languages don't seem
to provide a corresponding function in their standard libraries. If
you want to argue that it should be in the standard library, it would
help to see several different cases where it comes up in real Go code.
https://golang.org/doc/faq#x_in_std

Ian

Amarjeet Anand

unread,
Feb 15, 2020, 12:13:40 PM2/15/20
to Ian Lance Taylor, golang-nuts
Thanks ian for the quick reply.

As far as other languages are concerned,
Like Java, stringBuilder.reverse() seems a logical place to put this feature. Because string builder gives us facility to generate a string, overcoming the limitation of string immutability. What if I want to generate a string in reverse order?

Yeah, agree that its not difficult to implement.  

I need it quite often, maybe because of the kind of project am working on currently, but can't advocate that every go programmer needs them frequently.

Anyways, thanks for the clarification.

roger peppe

unread,
Feb 15, 2020, 2:10:33 PM2/15/20
to Amarjeet Anand, Ian Lance Taylor, golang-nuts
One reason for not implementing it in the standard library is that there are many possible implementations. Do you want to reverse combining character sequences for example? Unicode is a complex beast and I suspect that reversing a string rune-by-rune may easily break important semantics. Is it even possible to do it correctly in general ?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CANFuhy-S%2Bdoq0kq3KF_BuCCQYa9Zqn0OUQXfovrMk%2BiC7_A0%3Dw%40mail.gmail.com.

Juliusz Chroboczek

unread,
Feb 16, 2020, 4:43:33 PM2/16/20
to golan...@googlegroups.com
> One reason for not implementing it in the standard library is that there
> are many possible implementations. Do you want to reverse combining
> character sequences for example? Unicode is a complex beast and I suspect
> that reversing a string rune-by-rune may easily break important
> semantics.

What Roger is saying is that taking the sequence « e - combining acute »
and reversing it rune-by-rune yields a sequence starting with
a combining character, which doesn't make a lot of sense sense.

> Is it even possible to do it correctly in general ?

Depends how you define correct, obviously. If you want to ensure it's
invariant with respect to compatibility equivalence, you'll need to
normalise first (due to some compatibility characters normalising in
weird ways, for example U+0133 normalises to (U+0069, U+006A)). I'm not
sure if plain equivalence has similar pathologies.

> Unicode is a complex beast

Granted.

-- Juliusz

Axel Wagner

unread,
Feb 17, 2020, 5:53:51 AM2/17/20
to Amarjeet Anand, golang-nuts
Hi,

On Sat, Feb 15, 2020 at 6:13 PM Amarjeet Anand <amarjeeta...@gmail.com> wrote:
I need it quite often, maybe because of the kind of project am working on currently.

I'd be super curious what that is. TBH, given how frequently this has come up, I've been wrecking my brain trying to figure out what people need it for - even ignoring the problems Roger brought up. I promise, I'm not trying (or going) to argue that there are no cases where it's useful, just interested in the answer from someone who does need it :)
(FWIW, I do need to reverse sequences/arrays/slices quite frequently, just not strings, so that's the specific question)

howar...@gmail.com

unread,
Feb 18, 2020, 2:56:54 PM2/18/20
to golang-nuts
My most common use case for reversing strings has not come up in Go yet - basically, some languages provide a string search function that finds a character or substring starting from the beginning of the string. For example, in T-SQL, CharIndex. If they do not provide a corresponding search that works from the end towards the beginning, then removing something at the end of the string involves either searching to exhaustion then backing up to the last found index - this is easy in an imperative language like Go but trickier in a set-based language like SQL - or cheating by reversing the string, performing the search and modification, and reversing it again.

UPDATE Addresses SET Zip = SUBSTRING(Addr,LEN(Addr)-CHARINDEX(' ',REVERSE(Addr))+2,99)

Another use-case involves zero-filling a number to right align it (again in SQL) and ensure a proper field-size: 
REVERSE(SUBSTRING(REVERSE('000000000000' + UPC),1,12))

These are janky solutions that only work because I know the data being operated on is ASCII and just save doing some fiddly math instead. 

I guess this is neatly reflected by the fact that I only managed to find examples when I was searching the folder where I did my data conversions for customers - a search in the actual code found no instances.

I have not needed it in Go code so far.


ffm...@web.de

unread,
Feb 26, 2020, 9:21:12 AM2/26/20
to golang-nuts
Maybe the implementation in Java is something you could steal to save time. Have a look into class StringBuilder where there is a reverse() method. It does the reversion differently depending on whether dealing with UTF16 or not.

roger peppe

unread,
Feb 27, 2020, 1:53:01 PM2/27/20
to ffm...@web.de, golang-nuts
If you really just want to reverse rune-by-rune, it's pretty straightforward:

func Reverse(s string) string {
        r := make([]byte, 0, len(s))
        for len(s) > 0 {
                _, n := utf8.DecodeLastRuneInString(s)
                i := len(s) - n
                r = append(r, s[i:]...)
                s = s[:i]
        }
        return string(r)
}

That will also deal correctly with invalid utf8 encoding - all the bytes of the original string will be present in the result.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Message has been deleted

Dan Kortschak

unread,
Feb 27, 2020, 4:47:19 PM2/27/20
to Amnon Baron Cohen, golang-nuts
Why? There's a single correctly sized allocation made up front and then
a linear time walk along the encoded runes with truncation after each
rune.

On Thu, 2020-02-27 at 13:05 -0800, Amnon Baron Cohen wrote:
> O(n^2)
>
> On Thursday, 27 February 2020 18:53:01 UTC, rog wrote:
> > If you really just want to reverse rune-by-rune, it's pretty
> > straightforward:
> >
> > func Reverse(s string) string {
> > r := make([]byte, 0, len(s))
> > for len(s) > 0 {
> > _, n := utf8.DecodeLastRuneInString(s)
> > i := len(s) - n
> > r = append(r, s[i:]...)
> > s = s[:i]
> > }
> > return string(r)
> > }
> >
> > That will also deal correctly with invalid utf8 encoding - all the
> > bytes of the original string will be present in the result.
> > >
>
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/golang-nuts/2333bc33-8740-4f8b-972e-37d2d60b9dc7%40googlegroups.com
> .


Amnon Baron Cohen

unread,
Feb 27, 2020, 4:49:43 PM2/27/20
to golang-nuts
You are right.
I had wrongly assumed that utf8.DecodeLastRuneInString has O(n) runtime.
But it has constant runtime as it reads at most 4 bytes at the end of the string.



On Thursday, 27 February 2020 21:47:19 UTC, kortschak wrote:
Why? There's a single correctly sized allocation made up front and then
a linear time walk along the encoded runes with truncation after each
rune.

On Thu, 2020-02-27 at 13:05 -0800, Amnon Baron Cohen wrote:
> O(n^2)
>
> On Thursday, 27 February 2020 18:53:01 UTC, rog wrote:
> > If you really just want to reverse rune-by-rune, it's pretty
> > straightforward:
> >
> > func Reverse(s string) string {
> >         r := make([]byte, 0, len(s))
> >         for len(s) > 0 {
> >                 _, n := utf8.DecodeLastRuneInString(s)
> >                 i := len(s) - n
> >                 r = append(r, s[i:]...)
> >                 s = s[:i]
> >         }
> >         return string(r)
> > }
> >
> > That will also deal correctly with invalid utf8 encoding - all the
> > bytes of the original string will be present in the result.
> > >
>
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to golan...@googlegroups.com.

David Finkel

unread,
Feb 27, 2020, 5:25:51 PM2/27/20
to roger peppe, ffm...@web.de, golang-nuts
On Thu, Feb 27, 2020 at 1:52 PM roger peppe <rogp...@gmail.com> wrote:
If you really just want to reverse rune-by-rune, it's pretty straightforward:

func Reverse(s string) string {
        r := make([]byte, 0, len(s))
        for len(s) > 0 {
                _, n := utf8.DecodeLastRuneInString(s)
                i := len(s) - n
                r = append(r, s[i:]...)
                s = s[:i]
        }
        return string(r)
}

That will also deal correctly with invalid utf8 encoding - all the bytes of the original string will be present in the result.
 
Another option with slightly less bookkeeping (but slightly more magic indices):

func reverse(str string) string {
if len(str) == 0 {
return ""
}
out := make([]byte, len(str))
lastoffset := len(str)
for offset, r := range str {
rl := utf8.RuneLen(r)
copy(out[lastoffset-rl:lastoffset], str[offset:offset+rl])
lastoffset -= rl
}
return string(out)
}


On Wed, 26 Feb 2020 at 14:20, <ffm...@web.de> wrote:
Maybe the implementation in Java is something you could steal to save time. Have a look into class StringBuilder where there is a reverse() method. It does the reversion differently depending on whether dealing with UTF16 or not.

Am Samstag, 15. Februar 2020 17:37:15 UTC+1 schrieb Amarjeet Anand:
Hi

I was wondering why isn't there built-in string reverse function. Is it left intentionally because of some reason?

Although strings are immutable in go, there are multiple ways to achieve this pretty easily. But having this function inbuilt will save our time because we need it quite often.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/934265f6-666b-446b-aa5e-73f0b2bdcd78%40googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Jesper Louis Andersen

unread,
Feb 27, 2020, 6:17:52 PM2/27/20
to Amnon Baron Cohen, golang-nuts
The key observation is that you only look at a byte once.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/18eb08a5-f2be-452b-a31f-9643932fc4bd%40googlegroups.com.

Rob Pike

unread,
Feb 27, 2020, 6:23:57 PM2/27/20
to Jesper Louis Andersen, Amnon Baron Cohen, golang-nuts

Amnon Baron Cohen

unread,
Feb 27, 2020, 6:32:58 PM2/27/20
to golang-nuts
lol!

Where are you rob?
We miss you!


On Thursday, 27 February 2020 23:23:57 UTC, Rob 'Commander' Pike wrote:
Once bytten, twice shy.

-rob


roger peppe

unread,
Feb 28, 2020, 2:37:34 AM2/28/20
to David Finkel, ffm...@web.de, golang-nuts
On Thu, 27 Feb 2020 at 22:25, David Finkel <david....@gmail.com> wrote:


On Thu, Feb 27, 2020 at 1:52 PM roger peppe <rogp...@gmail.com> wrote:
If you really just want to reverse rune-by-rune, it's pretty straightforward:

func Reverse(s string) string {
        r := make([]byte, 0, len(s))
        for len(s) > 0 {
                _, n := utf8.DecodeLastRuneInString(s)
                i := len(s) - n
                r = append(r, s[i:]...)
                s = s[:i]
        }
        return string(r)
}

That will also deal correctly with invalid utf8 encoding - all the bytes of the original string will be present in the result.
 
Another option with slightly less bookkeeping (but slightly more magic indices):

func reverse(str string) string {
if len(str) == 0 {
return ""
}
out := make([]byte, len(str))
lastoffset := len(str)
for offset, r := range str {
rl := utf8.RuneLen(r)
copy(out[lastoffset-rl:lastoffset], str[offset:offset+rl])
lastoffset -= rl
}
return string(out)
}

I'm afraid that doesn't work correctly in the face of invalid utf8.

The reason is that when there's an invalid rune, only one byte is consumed, but
the rune is '\uFFFD' (utf8.RuneError) which encodes as more than one byte.

That said, consuming from the start is quite likely a better approach as I'm fairly
sure that `DecodeRune` will have received a great deal more optimisation effort
than `DecodeLastRune` which looks like it hasn't changed much since I wrote it
almost a decade ago :)

This does the job and does indeed seem to be somewhat (~10%) faster than going backwards:

func Reverse(s string) string {
        r := make([]byte, len(s))
        i := len(r)
        for {
                _, n := utf8.DecodeRuneInString(s)
                if n == 0 {
                        break
                }
                copy(r[i-n:], s[:n])
                i -= n
                s = s[n:]
        }
        return string(r)
}

  cheers,
    rog.

Amnon Baron Cohen

unread,
Feb 28, 2020, 3:23:40 AM2/28/20
to golang-nuts
Here is a dumb version, that wastes loads of memory.


func reverse(in string) string {
out := strings.Builder{}
out.Grow(len(in))
runes:= make([]rune, 0, len(in))

for _, r := range in {
runes = append(runes, r)
}
for i := len(runes) -1; i >= 0; i-- {
out.WriteRune(runes[i])
}
return out.String()
}

roger peppe

unread,
Feb 28, 2020, 3:45:33 AM2/28/20
to Amnon Baron Cohen, golang-nuts
On Fri, 28 Feb 2020 at 08:23, Amnon Baron Cohen <amn...@gmail.com> wrote:
Here is a dumb version, that wastes loads of memory.


func reverse(in string) string {
out := strings.Builder{}
out.Grow(len(in))
runes:= make([]rune, 0, len(in))

for _, r := range in {
runes = append(runes, r)
}
You might be interested to know that this operation is built in to Go itself, which means you can do something like this:

func Reverse(s string) string {
        runes := []rune(s)
        rev := make([]rune, 0, len(runes))
        for i := len(runes) - 1; i >= 0; i-- {
                rev = append(rev, runes[i])
        }
        return string(rev)
}

It's not even that much slower (about 60%). It doesn't always preserve the original string length though.


On Saturday, 15 February 2020 16:37:15 UTC, Amarjeet Anand wrote:
Hi

I was wondering why isn't there built-in string reverse function. Is it left intentionally because of some reason?

Although strings are immutable in go, there are multiple ways to achieve this pretty easily. But having this function inbuilt will save our time because we need it quite often.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Amnon Baron Cohen

unread,
Feb 28, 2020, 4:57:50 AM2/28/20
to golang-nuts
That's cool!

Of course we could reverse rune-slice in place, leaving a 7 liner...

func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r) - 1; i < j; i, j = i +1 , j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}


On Friday, 28 February 2020 08:45:33 UTC, rog wrote:
On Fri, 28 Feb 2020 at 08:23, Amnon Baron Cohen <amn...@gmail.com> wrote:
Here is a dumb version, that wastes loads of memory.


func reverse(in string) string {
out := strings.Builder{}
out.Grow(len(in))
runes:= make([]rune, 0, len(in))

for _, r := range in {
runes = append(runes, r)
}
You might be interested to know that this operation is built in to Go itself, which means you can do something like this:

func Reverse(s string) string {
        runes := []rune(s)
        rev := make([]rune, 0, len(runes))
        for i := len(runes) - 1; i >= 0; i-- {
                rev = append(rev, runes[i])
        }
        return string(rev)
}

It's not even that much slower (about 60%). It doesn't always preserve the original string length though.


On Saturday, 15 February 2020 16:37:15 UTC, Amarjeet Anand wrote:
Hi

I was wondering why isn't there built-in string reverse function. Is it left intentionally because of some reason?

Although strings are immutable in go, there are multiple ways to achieve this pretty easily. But having this function inbuilt will save our time because we need it quite often.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

Himanshu Makkar

unread,
Feb 28, 2020, 8:07:54 AM2/28/20
to golang-nuts
Hi

I think we can create a package to reverse a string and can use it whenever needed.

 reverse.go

package strutil
func Reverse(s string) string {
    runes := []rune(s)

    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}
come to your original .go programm and import that above reverse.go util package
package main
import(
       "fmt"
       "github.com/himanshu/go_code/strutil" .  // Path of reverse.go file
       )
 func main() {
                 fmt.println(strutil.Reverse("Hello World"))
               }     
It dosent take too much memory as well

Amnon Baron Cohen

unread,
Feb 28, 2020, 8:29:44 AM2/28/20
to golang-nuts
somebody beat us to it:
https://github.com/torden/go-strutil#reversestr

But for some strange reason, they seem to have made the this a method of a StringProc class.
Perhaps they used to code in Java.
Reply all
Reply to author
Forward
0 new messages