はじめに
ながすなりです。ちなみに私は説明できません!というか型安全って何?と自分に問いかけてみたところ満足な回答が返ってこなかったので、調べながらこの記事を書いています。理解が間違ってたら教えてくださいませ。
まず一言で
私は概念を理解するときまず一言で理解することを好みます。とりあえずそれは何?を理解することからスタートします。
型安全とは
プログラムが実行される際に、データ型に矛盾が生じないことを保証する性質のこと
プログラムの動作が正しく定義されるように保証するもの
↓のページにはこのように書かれていました。他のページも大体似たようなものなので、統合して一言で表すとすると
プログラムが実行される際に、データ型を保証すること
と書けるような気がしています。二つ目の記述の動作
という概念が抜けているのでは?と思われるかもしれませんが、原文の中で正しく型付けがされているー>不正な動作をしない
と記述されているので、大枠は同じことを言っていると思います。
つまり型安全とはデータ型を保証することであると考えられます。
データ型を保証するってなに?
じゃあよくわからないのは一つだけですね。
データ型を保証するってどういうこと?
この疑問にぶち当たるわけです。これが分かればどうやら型安全が分かるみたいです。
先ほど紹介したPOSTDでは続きにこう書いてあります。
言語の型システムは型安全な言語において”正しい”(間違っていない)プログラムだけが通過できるように保証する特別な方法
つまるところ正しいプログラムだけが通過するように保証することとデータ型を保証するというのはほとんど同義なのではないかと考えられます。じゃあ次の疑問です。
正しいプログラムあるいは正しくないプログラムって何?
こちらも記述がありまして、シンタックスは正しくともセマンティクスが正しくない状態のことを指すようです。簡単に言えば文法はただしいが、意味が正しくないというようなことでしょうか。例えば整数型と文字列型を加算してしまうようなことを想像していただければ大体あっていると思います。
このことをPOSTDの記事では不正な動作
と言っています。
つまり不正な動作をしないようなプログラムだけが通過するようにデータの型を保証すること
を単にデータ型を保証する
と言っていると考えてそう差支えはないように思います。
データの型があいまいであることで起こりうる不正な動作
が起こらないように事前にデータの型を保証することがどうやら型安全というものととらえて大きな問題はなさそうです。
gptちゃんに聞いてみました。間違っていなさそうですし、何ならGPTちゃんの回答のほうがわかりやすいですね。
型安全とは、「プログラムが想定外の型操作(不正な処理)を行えないように、言語仕様やコンパイラ、ランタイムなどによって型を厳密にチェックして保証すること」を指します。
その結果、不正なメモリアクセスや実行時エラー、バグなどを未然に防ぎやすくなります。
じゃあ実際にやってみん?
型安全じゃないと私の中でよく言われがちなC言語
と私の中で型最強のRust
君を比べてみます。
もっとわかりやすい例があればよかったのですが、ちょっと見つからなかったのでポインタを用いる例で行きます!int型で確保した領域をdouble型にキャストして参照している例を見てみます。
#include <stdio.h>
int main(void) {
int x = 42;
// int型のアドレスをdouble型にキャストして参照
printf("%f\n", *(double*)&x);
return 0;
}
実行結果
0.000000
Rustでは?
fn main() {
let x: i32 = 42;
// Cで言うところの *(double*)&x に相当する操作
println!("{}", *(&x as *const i32 as *const f64));
}
実行結果
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
--> Main.rs:6:20
|
6 | println!("{}", *(&x as *const i32 as *const f64));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
|
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0133`.
C言語では普通に実行されて、全く違う結果が出力されました。Rustではそもそもコンパイルが通りませんでしたね。
今回の例では「int型で確保した領域をdouble型として読み取る」という不正な動作
が起こらないようにRustではコンパイラが事前に型等をチェックして、保証したということになりますかね?
この事実からC言語
は型安全とは言いづらく、一方でRust
は型安全と言いやすいといえます。
他の言語で型安全なのってある?
さっくり調べてみました。厳密に言ったら違うけど...みたいなものがあったので薄目で参考にしていただけると幸いです。
型安全
- Java
- C#
- Rust
型安全でない
- C
- C++
と今ざっくり大体全体の意見が一致しているものだけピックしてみました。動的型付け言語についてはちょっと定義が揺れそうなので、ご自分で調べてみるとよいと思います...
まとめ
まず第一に型安全性自体も結構統一的な見解がないっぽいので一概に本記事が正しいとは到底言えないと思います。
型安全性とは「型エラーを起こさない」という性質である
こういう定義をなさる方もいますし、
コンパイルを通過したなら、ある種のバグが存在しないという性質
という定義をなさる方もいます。
最大公約数的な全員の行っていることの本質はずれてないよね?と考えられはしますが、微妙にニュアンスが違っていたりします。なので統一的な厳密な見解がないような気がしています。私が知らないだけで一つの定義があるのかもしれません。あったら教えてください。
ですが、本記事では
不正な動作
が起こらないように事前にデータの型を保証すること
を型安全の定義とし、私はその理解で生きていこうと思います。責任はもちろん持ちません。各々が各々で調べて自分なりに理解していただければと思います。願わくば統一的な見解が生まれることを、また私の間違いを誰かが指摘してくれることを。