cannot use type []rune as type rune in append
Asked Answered
V

1

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

Veronikaveronike answered 20/2, 2015 at 23:38 Comment(1)
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. :)Superintendency
S
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.

Superintendency answered 20/2, 2015 at 23:59 Comment(5)
How do I get it working if lettersLower and lettersUpper are constants?Veronikaveronike
If they were constants like const, they can only be strings, not []runes, 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.Superintendency
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...Salpingitis
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 gobyexample.com/strings-and-runes and go.dev/blog/stringsSuperintendency
And to get a rune constant value, you probably want single quotes: 'a'.Superintendency

© 2022 - 2024 — McMap. All rights reserved.