5
package main

var lettersLower = []rune("abcdefghijklmnopqrstuvwxyz")
var lettersUpper = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

func main() {
    x := append(lettersLower, lettersUpper)
}

Why does this not work? How can I append lettersLower and lettersUpper?

prog.go:7: cannot use lettersUpper (type []rune) as type rune in append

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

fnkr
  • 9,428
  • 6
  • 54
  • 61
  • Added a caveat about safely appending slices: unlike with strings, they let you mutate shared memory, so modifying one slice can surprisingly change the memory pointed to by another. Your example is fine, but I figure this is a useful warning to any `[]rune` appenders that may come across this page. :) – twotwotwo Feb 21 '15 at 00:20

1 Answers1

16

It's because append doesn't take a list to append, but rather one or more items to append. You can adapt to this with a ... on the second argument to append:

package main

import "fmt"

var lettersLower = []rune("abcdefghijklmnopqrstuvwxyz")
var lettersUpper = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

func main() {
    x := append(lettersLower, lettersUpper...)
    fmt.Println(len(x))
}

Try it out on the Playground.

Note that append does not always re-allocate the underlying array (that would cause problems in terms of performance and memory usage). You're fine as far as this sample goes, but it may bite you if you ever try to use the same memory for multiple purposes. A (contrived, perhaps unclear) example:

package main

import (
    "fmt"
    "os"
)

func main() {
    foo := []byte("this is a BIG OLD TEST!!\n")
    tst := []byte("little test")
    bar := append(foo[:10], tst...)

    // now bar is right, but foo is a mix of old and new text!
    fmt.Print("without copy, foo after:  ")
    os.Stdout.Write(foo)

    // ok, now the same exercise but with an explicit copy of foo
    foo = []byte("this is a BIG OLD TEST!!\n")
    bar = append([]byte(nil), foo[:10]...) // copies foo[:10]
    bar = append(bar, tst...)

    // this time we modified a copy, and foo is its original self
    fmt.Print("with a copy, foo after:   ")
    os.Stdout.Write(foo)
}

When you try to print foo after appending to a subslice of it, you get a weird mix of old and new content.

Where the shared underlying array is a problem, you could either use strings (string bytes are immutable, a pretty effective guard against accidental overwrites) or make a copy as I did with append([]byte(nil), foo[:10]...) above.

twotwotwo
  • 28,310
  • 8
  • 69
  • 56
  • How do I get it working if `lettersLower` and `lettersUpper` are constants? – fnkr Feb 21 '15 at 00:27
  • 2
    If they were constants like `const`, they can only be strings, not `[]rune`s, so you'd have to convert at runtime (or just not make them `const` in the first place, as you did). But the bit about slices being potentially shared memory wasn't meant to scare you off here--your `append` looks fine--just a general warning about how slices aren't exactly like strings. – twotwotwo Feb 21 '15 at 00:37
  • I don't understand I wrote `type T struct { v rune }` then I try to do ` t := T{v: "abc"[0]} ` with same error. Whats wrong with this language... – Verthais Oct 26 '22 at 18:43
  • Strings are read-only slices of (often UTF-8) bytes. You can convert a string to `[]rune` if you want indexing to return codepoints, at a cost in RAM. You can also get runes out with `for i, r := range s` or the `utf8` package. See https://gobyexample.com/strings-and-runes and https://go.dev/blog/strings – twotwotwo Oct 26 '22 at 19:17
  • And to get a `rune` constant value, you probably want single quotes: `'a'`. – twotwotwo Oct 26 '22 at 19:27