5

I'm trying to alter an existing string in Go but I keep getting this error "cannot assign to new_str[i]"

package main
import "fmt"

func ToUpper(str string) string {
    new_str := str
    for i:=0; i<len(str); i++{
        if str[i]>='a' && str[i]<='z'{
            chr:=uint8(rune(str[i])-'a'+'A')
            new_str[i]=chr
        }
    }
    return new_str
}

func main() {
    fmt.Println(ToUpper("cdsrgGDH7865fxgh"))
}

This is my code, I wish to change lowercase to uppercase but I cant alter the string. Why? How can I alter it?

P.S I wish to use ONLY the fmt package!

Thanks in advance.

jgritty
  • 11,660
  • 3
  • 38
  • 60
user3765713
  • 133
  • 2
  • 13

3 Answers3

8

You can't... they are immutable. From the Golang Language Specification:

Strings are immutable: once created, it is impossible to change the contents of a string.

You can however, cast it to a []byte slice and alter that:

func ToUpper(str string) string {
    new_str := []byte(str)
    for i := 0; i < len(str); i++ {
        if str[i] >= 'a' && str[i] <= 'z' {
            chr := uint8(rune(str[i]) - 'a' + 'A')
            new_str[i] = chr
        }
    }
    return string(new_str)
}

Working sample: http://play.golang.org/p/uZ_Gui7cYl

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
4

Use range and avoid unnecessary conversions and allocations. Strings are immutable. For example,

package main

import "fmt"

func ToUpper(s string) string {
    var b []byte
    for i, c := range s {
        if c >= 'a' && c <= 'z' {
            if b == nil {
                b = []byte(s)
            }
            b[i] = byte('A' + rune(c) - 'a')
        }
    }
    if b == nil {
        return s
    }
    return string(b)
}

func main() {
    fmt.Println(ToUpper("cdsrgGDH7865fxgh"))
}

Output:

CDSRGGDH7865FXGH
peterSO
  • 158,998
  • 31
  • 281
  • 276
3

In Go strings are immutable. Here is one very bad way of doing what you want (playground)

package main

import "fmt"

func ToUpper(str string) string {
    new_str := ""
    for i := 0; i < len(str); i++ {
        chr := str[i]
        if chr >= 'a' && chr <= 'z' {
            chr = chr - 'a' + 'A'
        }
        new_str += string(chr)
    }
    return new_str
}

func main() {
    fmt.Println(ToUpper("cdsrgGDH7865fxgh"))
}

This is bad because

  • you are treating your string as characters - what if it is UTF-8? Using range str is the way to go
  • appending to strings is slow - lots of allocations - a bytes.Buffer would be a good idea
  • there is a very good library routine to do this already strings.ToUpper

It is worth exploring the line new_str += string(chr) a bit more. Strings are immutable, so what this does is make a new string with the chr on the end, it doesn't extend the old string. This is wildly inefficient for long strings as the allocated memory will tend to the square of the string length.

Next time just use strings.ToUpper!

Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132