https://youtu.be/1K5_Q_YyezI
今回も引き続きThe Basics
の 数値型変換
について眺めていきます。前回は数値型変換の必要性を感じ取るための前段階、整数の表現力に意識を向ける回でしたけれど、今回は実際に型変換を行う方法とその意義的な話に触れる感じになりそうです。どうぞよろしくお願いしますね。
————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #121
00:00 開始 01:47 StaticString の印象 02:37 StaticString の特徴 04:49 StaticString が備える機能 05:37 文字列補完構文は使えない 06:06 StaticString を使う価値は? 08:38 結局はランタイムに持ち越される? 09:01 StaticString のサイズ 10:35 StringProtocol に準拠していない 11:36 部分文字列に関するアップデート 12:10 StringProtocol を想定する場面で使えない 13:22 StaticString を活用できるためには 17:28 StaticString についての余韻 18:31 値の範囲を合わせるために型変換が必要 19:56 変換イニシャライザー 20:52 明示型変換 21:36 型キャストと型変換 23:12 型キャストの種類 24:39 型変換について詳しく見てみる 25:53 型強制? 26:34 アップキャストとダウンキャスト 27:57 クロスキャスト? 28:09 静的キャスト 28:38 不変性キャスト 29:40 再解釈キャスト 30:25 動的キャスト 31:27 Swift における静的キャストの流儀 32:33 丸め誤差には注意 34:26 次回の展望 —————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #121
それでは始めていきましょう。今日は型変換のお話です。少し前回の内容を振り返りつつ、実際の型変換のお話に入ります。その前に、前回予告したStatic Stringについてお話しします。少し調べてみたのですが、いまいち重要性がわからず、何かあればお話ししようと思っていました。
Static Stringについて調べたところ、あまり活用する場面が見当たらなかったので、その点についてもお話ししておきます。Static Stringは、コンパイラーが最適化を図るための機能で、print
などでStatic String
を表示することができます。しかし、どんな最適化が図られているのかが不明瞭です。
調べた内容としては、Static Stringは標準ライブラリーに用意されていて、コンパイルタイムに分かるものを明記するための型です。このため、コンパイルタイムにエラーチェックが行えるというメリットがあります。ただし、文字列操作に関しては、コンパイルタイムにやっておいしいことはあまりないように感じました。
また、Static Stringが持つ特徴として、文字列リテラルがメモリに展開されるアドレス情報だけで表現できるという点があります。実際にはUTF-8で格納され、ポインターでアクセスされています。また、ASCIIかどうかの判定もできますが、これはコンピューティブプロパティを利用しています。
一方で、StringInterpolation
はランタイムに処理が持ち越されるため、Static Stringでは使用できません。そのため、普通のStringリテラルで済ませる方が良い場面が多く、Static Stringを使うメリットが少ないように感じます。
たとえば、UserDefaultsで使おうとした場合、Static StringはString型に変換されなければならず、その際にインスタントで作られたString型の値が必要になります。これによりコードの明瞭性が損なわれることがあります。要は、実際に使ってみるとランタイムに持ち越されてしまうことが多いので、Static Stringの使い勝手が微妙です。
総じて、メモリーサイズの管理や効率化を狙った場合には差が出るかもしれませんが、大きな違いはないかと思います。たとえば、String型とStatic String型でメモリーサイズを比較しても、わずか1バイト程度の差しかありません。理論的にはStatic Stringで得られるメモリー効率の良さがあるものの、実際にはそこまで大きなメリットは感じられません。
Swiftの型変換については以上で、次に進んでいきましょう。 Swiftの言語仕様について話している内容を整理します。
まず、文字列に関連する部分ですが、24バイトの差が割と大きいと感じるという話をしています。これが影響を及ぼす部分は画面にも出てくるかもしれません。これによってスタティックストリングへの興味がわかないという意見が出されています。
そして、もう一つ微妙だと感じた点として、文字列(String)があり、Stringプロトコル
に準拠することで型を気にせず使用できることが挙げられています。普通の文字列も渡せるし、サブストリング(Substring)も渡せるという点が重要です。例えば、新しいバージョンのSwiftではString
のstartIndex
を使って文字列操作を簡単にすることができます。しかし、インデックスを取ってそれを使うという操作は多少煩雑になることがあります。
String
プロトコルに対して、普通の文字列型やサブストリング型も渡せるというのが基本です。しかし、サブストリングに関しては、使用の際にString
型として渡さないといけないため、使い勝手が悪いという問題があります。これでは、コンパイルタイムにメモリー最適化が図られたとしても、便利さに欠けていると感じられます。
スタティックストリングの真価は、コンパイルタイムで完結することによって何かメリットが生まれなければならないとしています。例えば、let a: StaticString
とlet b: StaticString
があったときに、let c: StaticString = a + b
のような操作ができないなら、その利用価値は低くなります。
最後に触れた@cost
は、新しいSwiftのコンパイルタイム最適化を行うためのマークですが、現段階ではまだ試せない状況です。これが導入されても、すぐに役に立つという状況が整うのはまだ先のことのようです。
以上のポイントから、現状ではスタティックストリングや文字列操作における課題についての意見が中心に議論されています。 ただ、Swiftエボリューションのいわゆる提案を見る限り、将来的にはコンパイルタイムのアサーションが追加されたりと、様々な展望があるようです。演算に関しても同様です。例えば、Int.max + 1
のオーバーフローをコンパイルタイムで検出できるようになる可能性があります。このような展望からすると、将来的にコンパイラが演算を必ずしもランタイムに持ち越さず、コンパイルタイムに解釈できるようになることが主流になるかもしれません。
その観点からすると、このスタティックストリングの活用場面が、2~3年先くらいに増えてくるのではないかと感じています。その時にうまく使えるように、今からイメージ作りをしておくことも重要かもしません。将来的にはスタティックストリングがコンストストリングに変わるのではないかとも考えられます。未来を見据えた立場の考え方という印象もあります。
初見では、スタティックストリングはかっこよさそうに見えますが、実際に使ってみるとあまり見かけないような印象でした。自分が調べた限りでも、もっと良い使い方があるかもしれないと思います。ただ、これからの結果が楽しみですね。スタティックストリングに関する話はここまでです。
さて、本題に入ります。今回は型変換の話をしましょう。前回のおさらいも特に必要ないと思いますので、このまま進めます。
型変換において、特に整数型の変換に注目しています。前回の話と繋がる部分ですが、各整数型(例えば、Int8
や UInt16
など)には異なる値の範囲があります。状況に応じてこの値の範囲を適切に調整する必要があり、そのために型変換が必要です。
型変換を行う場合、特に明示的に行う必要があります。明示的に型変換を行うアプローチは、思わぬところで値が範囲外になり、丸められてしまう変換エラーを防ぐために重要です。コード内で変換の意図を明確にすることもできます。
型変換の考え方について、微妙な表現や非常に曖昧な表現が多く見受けられますが、理解を深めるためには重要なポイントです。 特定の数値を別の型に変換するには、既存の値を目的の新しい型の新しい数値で初期化します。要は、変換先の型の値を変換元の値で初期化するということです。これについて、プレイグラウンドで詳しく見ていきましょう。
次の例では、2000
はUInt16型
で、1
はUInt8型
です。型が合わないため、直接表示できません。したがって、型を合わせる必要があります。
let number1: UInt16 = 2000
let number2: UInt8 = 1
let combinedNumber = number1 + UInt16(number2)
この例を使って、プレイグラウンドでゆっくり見ていきましょう。隠れた型変換エラーを防ぎ、コード内で変換の意図を明示するのに役立ちます。型変換がなかった頃に比べれば、安全性が増し、エラーを補足できる猶予が生まれています。
型変換という言葉を挙げると、他の言語を使っている人にとっては「型キャスト」という言葉がなじみ深いかもしれません。言語によっては、型キャストと型変換という言葉をそんなに区別なく使うことが多いです。型キャストと型変換という言葉が区別なく使われることもありますが、Swiftでは型変換がはっきりと明確に扱われています。
具体的な例を示すと、例えばC言語では型キャストが行われます。変数int型
があり、それをdouble型
の変数に暗黙変換することができます。Swiftでも、同様に型変換を行いますが、Swiftでは明示的に行うことが推奨されています。これによりコードの安全性が向上し、エラーを回避できるようになります。 暗黙変換できたとすると、こうですよね。もし暗黙変換できなかったとすると、明示キャストをする必要があります。この場合のキャストについて、いくつか種類がありますよね。例えば、C++だとよく見るのがインタープリットキャスト、ダイナミックキャスト、スタティックキャスト、リプレゼントキャストなどです。
Wikipediaによると、「型変換」として紹介されています。この言葉は非常に曖昧で、「キャスト」と言われると具体的なイメージが湧かないことがありますが、自分なりに整理してみます。まずは、4種類だったと思うので、そのあたりをWikipediaで確認してみましょう。
型変換はプログラムにおいて、一つのデータ型から他のデータ型に変換することを指します。これは「型キャスト」とも呼ばれます。例えば、コンパイラーが自動で行う変換を暗黙型変換と呼びます。暗黙型変換は限定的で、互換性のあるもので行われます。これに対して明示的に型変換を指定するのは、明示型変換です。
つまり、暗黙の型変換が存在する場合、それは互換性のあるものに対して行われます。それ以外の場合、明示的に型変換を指定する必要があります。これを型強制とも呼びます。
このように、キャストにはいくつかの分類があり、例えばアップキャストは、ベースクラスと継承クラスの間で互換性が確実にあるために行われます。この場合、暗黙キャストができるわけです。Swiftでも同様に、アップキャストが可能です。
一方で、C++ではアップキャストが安全でない場合があるため、ダイナミックキャストが推奨されています。ダイナミックキャストは、互換性があるか分からない場合にランタイム上でキャストを行いますが、Swiftではこのようなダイナミックキャストもキャストという言葉で統一されます。
さらに、プロスキャスト、スタティックキャスト、コンソーキャストなども存在します。これらは例えば、整数同士の型変換や、不動小数点数と整数間の変換など、一般的な型変換に用いられます。
以上、キャストの種類とその使い方について簡単に整理しましたが、これを踏まえてさらに具体的な例や詳細について見ていきましょう。 例えば、C++では「キャスト」が問題になる場合があります。場合によってはキャストを外したほうが便利なことがありますが、それが許されるかどうかは別の問題です。Swiftでは、そうしたコンソール的なミューテイティング、つまりイミュータブルな型を勝手にミュータブルに変更するような危険なキャストは基本的に存在していません。
「リプレゼントキャスト」はビットを再解釈してキャストする方法です。Swiftで型変換を行う際には、例えばInt
型があるとすると、そのビットパターンを理解して変換するものです。この変換は「ナロータイプコンバージョン」と分類され、一応型変換に入りますが、慎重に扱う必要があります。
「アズエンダー」などのダイナミックキャストは型変換には含まれません。たとえば、object as? String
のような形でオブジェクトを別の型にキャストします。「インタープリットキャスト」は再解釈の変換で、通常のキャストとは異なります。Int
型をDouble
型に変換する際には、ビット配列が異なるため再解釈が必要ですが、Swiftではイニシャライザーを使って適切に型変換を実装することが求められます。
C++にはキャストに関連した問題が多く、C++11からは特にキャストの仕様が厳格になりました。Swiftでも、型変換は基本的にイニシャライザーで提供され、明示的に変換位置を記述することで、安全にコードを書くことができるようになっています。
例えば、Int
型の最大値をDouble
型に変換しようとすると、大きすぎてDouble
型では正確に表現できませんが、それでも変換が実行される場合があります。このような場合、暗黙の型変換に頼りすぎず、明示的にエラーを通知する型変換が理想ですが、それでも完璧には防ぎきれないことがあります。
現時点では、Swiftの型変換は安全性が向上しているとはいえ、ある程度のエラーは避けられないこともあります。しかし、他の言語と比較してもかなり改善されているので、上手に活用していけば良いと思います。
時間が来ましたので、今日の勉強会はこれで終わりにします。次回、実際にどのように型変換を活用するのかについての詳細な話を進めていきたいと思います。お疲れ様でした。ありがとうございました。