rune型は単なるint32のエイリアス

runeの疑問

rangeでrune型を取り出す際

func main() {
  str := "A"
    var x rune
    for i, v := range str {
        x = v
        x = str[i] // 代入出来ない
    }
}

値はrune型。けどインデックスアクセスで取得した値はrune型じゃない。

どちらもstring()でキャストしたらAなのに何で?

型を調べる

func main() {
    str := "A"
    for i, v := range str {
        fmt.Printf("str[i]  value: %v, type: %T \n" , str[i], str[i])
        fmt.Printf("v       value: %v, type: %T" , v, v)
    }
}

結果

str[i]  value: 65, type: uint8 
v       value: 65, type: int32

値はどちらも65

しかし型は相違。インデックスアクセスの場合はuint8, vの場合はint32

実はこれらはbyteruneエイリアスになっていて、

var x rune = int32(1)
var y byte = uint8(1)

この様に書いてもコンパイルエラーにならない。

''

シングルクオートで囲った文字列もrune型として出力

'A' // 65

rangeの値と一緒。どちらもint32の65を示す

func main() {
    str := "A"
    for _, v := range str {
        if v == 'A' {}  // true
    }
}

逆に言うと、int32(65)をstringでcastするとAという文字列を取得できる

fmt.Println(string(int32(65))) // A

個人的には結構衝撃的だった

アルファベットを出力する問題

runeとstirngを使った良い例題

問題

英大文字のみからなる文字列 S があります。また、整数 N が与えられます。 S の各文字を、アルファベット順で N 個後の文字に置き換えた文字列を出力してください。 ただしアルファベット順で Z の 1 個後の文字は A とみなします。

直感的にとくと

str := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
m := map[string]int{}

for i, v := range str {
    m[string(v)] = i
}

var ans string
for _, v := range s {
    i := m[string(v)]
    target := (i+n) % 26
    ans += string(str[target])
}
fmt.Println(ans)

値とindexを反転させたmapを作って、各値に対応したindexにnを足した値をSから求める

しかしruneを使えばもっとシンプルに解ける

参考解答

func main() {
    scanner := makeScanner(16384)
    n := eGetInt(scanner)
    s := eGetLine(scanner)
    rs := make([]byte, len(s))
    alpha := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    for i, c := range s {
        order := c - 'A'
        slide := (int(order) + n) % 26
        rs[i] = alpha[slide]
    }
    fmt.Println(string(rs))
}

アルファベットのruneは連番になっている為、対象文字列をrangeで回して値-Aのrune値を算出すれば、それがアルファベットの何番目の文字かint32型で取得できる。

この時取得した値にNを足し、26で割ったあまりが欲しいアルファベットのインデックス。alpha[index番号]で得られる値はint32ではなくuint8なのでbyte配列に結果を代入し最後にキャストする

まとめ

  • byteとruneはuint8 int32のエイリアス
  • アルファベットのrune型は連番
  • stringをrangeで回した場合、得られる値はインデックスアクセスして取得した値と型が違う