【Go言語】今更聞けない? 型とリテラル
こんにちは、めんだこです!
今回は、
リテラルってたまに聞くけど、型と何が違うの?
という、静的型付け言語の初学者さんを主な対象として、
型とリテラル
について解説していきます。
私自身、型とリテラルの概念が参考書を一読しただけではわかりませんでした。
2つを合わせて説明した方が個人的には理解しやすかったので、私流に説明させていただきます!
どうぞよろしくお願いします!
結論
例えば、こんな変数を宣言したとします。
var x int = 100
var y string = "Hello, world"
この場合、型とリテラルは、こんな具合に分けられます。
あれ?それだけと思った方。
それだけです。
しかし、これだけでは抽象的すぎるので、もう少し具体的に言い換えると、リテラルとは
値の書き方のお作法(文法)と言い換えても良いかもしれません。
しかし、私はこれを初め「型」の概念と混同していました。
以降は、リテラルと型を混同してしまいがちな理由について解説していきます。
リテラルについて
初学者の頭を悩ませて、混乱の原因になるのは
リテラルにも型のような種類がある
ということだと思います。
Go言語のリテラルの種類は、次のようなものがあります。
リテラル | 表現 |
整数リテラル | 10 1_000 など |
浮動小数点数リテラル | 3.1415 1.6e4 (1600) など |
文字列リテラル | “Hello, world” `改行 したい` (“改行\nしたい”)など |
etc.. |
整数リテラルは、半角数字(0~9)と「_」で記述され、文字列リテラルは「””」の中に文字を記述していく、といった具合です。
注意すべき点は、リテラルはあくまで表現方法であり、変数の型とは直接関係ないということです。
このことに気づく以前の私は、
- 整数リテラルは、int型
- 文字列リテラルは、string型
と、それぞれ同じものだと思っていました。。
型をきちんと理解するために、今度は型とリテラルの違いに注目して、お互いの関係について見ていきましょう。
Go言語で、基本型に使われるリテラルには
- 数値(2種類)
- 整数リテラル
- 浮動小数点数リテラル
- rune(文字)リテラル
- 文字列リテラル
- 複素数リテラル
の5つがあります。
(それぞれの詳細は、別の解説記事に譲ります)
- あれ?だけどなんか足りなくない?
-
「ture」,「 false」は?
と思った方。お目が高い。
公式の仕様には、次のようにあります。
The boolean truth values are represented by the predeclared constants true and false.
https://go.dev/ref/spec#Constants意訳すると
ブール値の真理値は、あらかじめ宣言された定数trueとfalseで表されます。
定数に関しての解説記事も今後書く予定ですので、詳しい解説はそちらでさせていただきますが、今は
「trueとfalseはリテラルじゃなくて、定数なんだ〜」
と思っていただければ良いと思います。
基本型以外に使われるリテラルもあり、
- 関数リテラル
- 構造体リテラル
- mapリテラル
など、Go言語のそれぞれの文法の記述方法を定義するリテラルが存在します。
型とリテラル
最初の説明を振り返ってみましょう。
型とリテラルは、このような関係でした。
この関係と、リテラルの種類を踏まえると、それぞれの変数に代入する値は
『型に合う』リテラル
で表現されていることがわかります。
- 100は「整数リテラル」 -> int型の変数に代入できる
- “Hello, world”は「文字列リテラル」 -> string型の変数に代入できる
少しわかりやすく整理すると、
「型」とは、変数という箱の”種類”を指定し
「リテラル」は、その箱の”中身”を指定する
と考えると良いかもしれません。
おもちゃ箱に包丁を入れると、子供は怪我をしてしまいます。
Go言語などの静的型付け言語では、規定された型に合わないリテラルが代入されると、コンパイル時にエラーで知らせてくれるのです。
先ほどから『型に合う』という曖昧な表現をしてきましたが、ここでそれぞれのリテラルがデフォルトでは、どの型と判断されるのかを見てみましょう。
fmt.Printf("整数リテラル:%T\n", 10)
fmt.Printf("浮動小数点数リテラル:%T\n", 10.0)
fmt.Printf("文字列リテラル:%T\n", "文字列")
fmt.Printf("runeリテラル:%T\n", 'a')
fmt.Printf("複素数リテラル:%T\n", 1+3i)
出力は次のようになります。
go run main.go
整数リテラル:int
浮動小数点数リテラル:float64
文字列リテラル:string
runeリテラル:int32
複素数リテラル:complex128
(runeリテラルがint32型として判断されることを不思議に思う方は、ぜひ例題をご覧ください)
例えば次のようなコードは型とリテラルが『合わない』ため、コンパイルエラーが発生します。
var x int = "Hello, world" // compile error
型についての詳しい解説はここではしませんが、このように「型」と「リテラル」は互いに異なる概念であることが分かっていただけたと思います。
最後にいくつか例題も用意しましたので、もっと理解を深めたい方はぜひお試しください!
例題
次のコードの出力を考えてみましょう(エラーの場合もあります)。
例題1
var x int = 10.0
fmt.Println(x)
- 解答
-
10
int型の変数ですが「浮動小数点数リテラル」の値は代入できます。
これは、リテラル自体には「型がない」ためで、リテラルと互換性を持つ任意の変数に代入できます。
ただし今回のような場合は、小数点以下の値は0である必要があります。
例題2
var x int = 10.5
fmt.Println(x)
- 解答
-
cannot use 10.5 (untyped float constant) as int value in variable declaration (truncated)
(意訳):変数宣言で10.5(型付きでない浮動小数点数定数)をint値として使用できません(切り捨て)。
例題1で見たように、int型の変数に「浮動小数点数リテラル」で値を代入する場合は、小数点以下の値は0である必要があります。
例題3
var x float64 = 10
fmt.Println(x)
- 解答
-
10
逆に、float型の変数に「整数リテラル」の値も代入することができます。
例題4(おまけ)
var x rune = 'a'
fmt.Println(x)
- 解答
-
97
これは、誰も当たりませんね(笑)
runeリテラルの公式の仕様を見てみましょう。
A rune literal represents a rune constant, an integer value identifying a Unicode code point.
https://go.dev/ref/spec#Rune_literals意訳すると
runeリテラルは、rune定数を表し、Unicodeを識別する整数値です。
となります。
実際にUnicodeにおける「a」について調べてみると、
確かに、UnicodeのNoが「97」となっていますね。
本題とは少しそれますが、rune型はint32の別名(エイリアス)です(byteがuint8のエイリアスであるのと同様です)。
このように、rune型はUnicodeの番号を返しますが、実際の実装で使う機会あまりないでしょう。
参考文献
こちらの記事の執筆にあたり、以下のWebサイト・書籍を参考・利用させていただきました。
ありがとうございました!
【webサイト】
- The Go Programming Language Specification (Google)
- CODEPOINTS (Manuel Strehl)
- GO言語(golang)における各リテラルの型を表示する (tmrtmhr)
【書籍】
- 初めてのGo言語(著者: Jon Bodner, 発行所: 株式会社オライリー・ジャパン 発行日: 2022.09.22)
- Go言語ハンズオン(著者: 掌田 津揶乃, 発行所: 株式会社 秀和システム, 発行日: 2021.03.10)
今回は、ご覧いただきありがとうございました。
また、次の記事でお会いしましょう!では!
より良い記事を提供するため、この記事に対するご指摘等ございましたら随時コメントを募集しています。
お気軽に書き込みください。コメントお待ちしております!