Which approach is better, and does it matter?

I’m a beginner that studied 2 books about Go, and I’d say I know the coding a bit. But something that I still struggle with (also from a confidence standpoint) is choosing amongst different options.

Say for instance I want to reverse a string. I found/wrote at least 3 ways to do that today:

func reverseString(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)
}
func reverseString2(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
func reverseString3(s string) string {
	size := len(s)
	buf := make([]byte, size)
	for start := 0; start < size; {
		r, n := utf8.DecodeRuneInString(s[start:])
		start += n
		utf8.EncodeRune(buf[size-start:], r)
	}
	return string(buf)
}

My questions:

  • How do I know or can tell which of these approaches is ‘the best’?
  • Does it matter which approach I use? (Given how performance-oriented Go is, I’d say yes.)
  • And from a broader perspective: how to deal with these ambiguous situations as a beginner?

First you have to determine how to measure your options. Speed ? Memory consumption ? ease of programming ?

HTH,
Yamil

The best approach would be the one in which you don’t convert from string to []byte and back, meaning reverseString3.

Good question. But I don’t really know. I don’t expect the speed of the three options to be very different. The same for the memory. So I’d suspect that ease of programming outweighs those other factors?

Ah I didn’t knew that. Thanks. Are the []rune arrays (and their operations) from the first two options are much more expensive than calling two additional methods? (DecodeRuneInString(), EncodeRune()).

Just made my first benchmark test in trying to figure it out. :slight_smile:

These are the results on my pc:

C:\Go practice\strings\stringtest>go test -bench=. -benchmem
goos: windows
goarch: amd64
BenchmarkReverseString-4         2000000               967 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString2-4        2000000               987 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString3-4        2000000               731 ns/op              96 B/op          2 allocs/op
PASS
ok      _/C_/Go_practice/strings/stringtest   8.121s

The third option is indeed quicker as @badu already knew. :slight_smile: The first and second option are the same.

So from a performance perspective the third option is not good. But from a code readability I think this option is most difficult. So the ease of programming that Yamil pointed out is less good.

Indeed, you should prefer readability over performance, until you are being asked to improve it. However, when being asked to improve performance, one should know which is the pedal-to-the-metal :slight_smile:

1 Like

Thanks for your help and insights. :slight_smile:

Have a nice day!

Please be aware that the third function may panic if the string contains an invalid UTF-8 byte sequence, e.g.:

reverseString3("\x80")

https://play.golang.org/p/h5D0kaOZ6w_E

1 Like

Thank you, that’s a very good catch. Much appreciated that you took the time to comment. :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.