ど〜も、てるし〜です。Qiitaでは初登場です。
いつもはzennに登場していますが、なんとなくQiitaで書いてみようと思いました。
背景
最近、Rustを勉強してます。
元々、JSやTSを書いている私でしたが、成長が止まったなと思ってRustを勉強し始めました。
勉強方法としては以下のドキュメントを読むことをしていました。
上記ドキュメントはプログラマーが「面白い!」と思うようなことがたくさん書いてあります。
私自身、まだ読み途中なのですが読むたびに「😁😁😁😁😁😁😁」って感じになります。
実際にプロジェクト等で使っているわけではないのですが、使えそうなところは考え方
その中でいくつか良いなと思ったこと、いくつかをTSに持ち込んだ話をします。
ぶっ叩くなら隠れて叩いてね😇
1. 型
TSをやっていた私がRustをやって思ったことは型がTSより明確だということです。
そもそも、JS/TSでtypeof a
とやった時にa
の答えになり得る値は
- bigint
- boolean
- function
- object
- number
- string
- symbol
- undefined
だったと思います。
みなさんはこれを見て何か感じますか????
ではRustの型ですが、整数型だけでなんと10種類あります。
しかも、これは整数型でその他に数字だと小数系があります。
対してTSやJSはnumber
でまとめられています。
出力結果を知っていて書いていればいいですが、想定外の結果が返ってくる時点でJSやTSの考えは良くないのではないかなと思っています。
2. 変数
変数という言葉を聞くとみなさんは何を思い浮かべますか???
Rustの変数の章を読んだ時、私は「😵😵😵😵😵😵😵😵😵😵😵😵😵」って感じになりました。
そもそも変数には不変変数と可変変数というものが存在しているのです!!
不変変数、可変変数はざっくりいうと再代入できるかできないかです。
Rustでは変数宣言をするときにlet
を使います。
let num_first=1;
この場合let
のみを宣言すると再代入が不可能となるため不変変数という形になります。
可変にするには
let mut num_first=1;
num_first=2;
という形にします。
mut
をつけないと再代入をする際にコンパイラに怒られます。
元々値が変わらないことを宣言しているのにも関わらず、値を変更しようとすると怒られるのです。
「これは再代入をしませんよ」ということをコード上で宣言できることはとても良いことだと思います!
さらに、エラーを出してくれるということはとても幸せなことで予期せぬバグを防いでくれることにつながるのでとてもハッピーです!!
ここでみなさんは疑問に思いませんか?
じゃあ定数は???と。
Rustに定数というものはもちろん存在します。
Rustでの定数は
- 常に不変
- 必ず型を注釈しなければならない
- 定数式にしかセットできない
という特徴があります。
const MAX_NUM:u32=100000;
プログラムを実行中に全く値が変わらない値を定義するのにとても役に立ちます。
よく、変わらない値をハードコードしている人がいると思いますが、定数を使って値を定義しておき条件式に利用することでコードの可読性やメンテナンスがしやすくなると思います。
これはどの言語においても言えることかと
これを学んだ時私はとあることに気づきました。
「JS/TSって定数って概念ねーじゃん。。。」
MDNには一応定数ってことは書いてありますが、オブジェクトの値を足したり消したり、リスト(一応配列って言っているらしいが)を伸び縮みさせたりとできるのでそれは定数ではなく不変変数ではないのかと思っています。
そもそもJS/TSでは伸び縮みできる複数の値を入れることが可能なものを配列と呼んでいますが、実際の配列は伸び縮みが不可能で同じ型を持つ値の集まりだと思います。
プログラミングの一般的な考えとはなんか違うような気がしてます。。(あくまでも個人的な意見です)
3. 所有権
Rustのドキュメントで所有権に入った時私は1週間くらい「😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇」って感じになってました。
JSやTSを扱っているとメモリの意識はしないんじゃないですかね(私自身がそもそもあまり意識をしていなかったので)
所有権について書いてある記事はたくさんあるのでここではあまり語りませんが、
C言語では速度は速いがメモリを手動で操作をするのでミスが起こりやすい、逆にJavaやpythonのようなガベージコレクションでメモリ操作を勝手にやってくれるが速度が遅いのと勝手に解放されるのでどのタイミングで解放されるのかがよくわからない(だったかな?)のでそれぞれう〜〜〜んと思うところが多いのに対し値を所持する変数は必ず1つというRustの所有権のルールは私はとても感動しました。
所有権を学ぶことはメモリがどうこうという話なので今まで意識していなかった部分を意識できるようになる良いきっかけになるのではないかなと思います。
個人的にはとても面白い!と思える内容でした。
4. null
とOption型
もしくはResult型
みなさんはプロジェクトで、null
を使っていますか??
私は堂々と使っていました。ですが、ハンドリング等を怠ってかなりバグを起こした痛い記憶があります。
Rustのenum
の単元で面白い話が出てきました。
Null References: The Billion Dollar Mistake"(Null参照: 10億ドルの間違い)
これの詳細はこのように書いてあります。
nullの開発者であるトニー・ホーア(Tony Hoare)の2009年のプレゼンテーション、 "Null References: The Billion Dollar Mistake"(Null参照: 10億ドルの間違い)では、こんなことが語られています。
私はそれを10億ドルの失敗と呼んでいます。その頃、私は、オブジェクト指向言語の参照に対する、 最初のわかりやすい型システムを設計していました。私の目標は、 どんな参照の使用も全て完全に安全であるべきことを、コンパイラにそのチェックを自動で行ってもらって保証することだったのです。 しかし、null参照を入れるという誘惑に打ち勝つことができませんでした。それは、単純に実装が非常に容易だったからです。 これが無数のエラーや脆弱性、システムクラッシュにつながり、過去40年で10億ドルの苦痛や損害を引き起こしたであろうということなのです。
null
は予期せぬバグ、下手すると脆弱性に繋がってしまうことがあります。
Javaなどの言語ではnull
で犯したミスはエラーではなく実行時のバグとして起こってしまいます。
null
は実装が楽なのかもしれませんが、しっかりハンドリングをしないと実行時に大変なことになってしまいます。
近年、開発されている言語ではnull
がないものがほとんどだと思います。
Rustもその一つです。
RustではOption
型やResult
型などnull
の代わりにコンパイラが明確にわかる型設計が用意されています。
Option
型にはsome
とnone
がありsome
の中には存在する値が格納されています。
また、これらの型はハンドリングしないと明確な値が出てこないといったことからバグを未然に防げる安全なソースコードが生まれやすいともいえます。
この考えはJS/TSでも私は持ち込むようにしました。「match
やif let
がない」という意見はありますが、if
やswitch
でハンドリングしてから次の処理に進むという意識付けができるようになり、しょうもないバグが減るようになりました。
少し前に書いた記事をここで記載しておきます。
TSではResult
型のライブラリがあるらしいですが、自分はよく簡単なOption
型とResult
型を作ります。
interface Some<T>{
kind: "some",
value:T
}
interface None{
kind:"none"
}
type Option<T>=Some<T>|None
イメージだけを書きましたがnull
からOption
に書き換えるだけで確実にハンドリングをしないといけない状況になるので個人的には導入してみても良いんじゃないかな?と思います。
まとめ
いかがだったでしょうか?
Rustはとても個人的に良い言語だと思いますし、他の言語で行き詰まったり成長が止まったと感じた時に勉強すべき言語とも言えるのかもしれません。
Rustは安全なコードを書けるような設計をしているように感じられました。
特にコンパイラが細かくエラーを出してくれるのでそこがとても良いなと感じる瞬間でした。
今回4つのことについてを取り上げましたが他にもジェネリックなデータ型やエンコーディングについてなどRustを勉強することで今まで意識していなかったことが意識できるようになる可能性が高くなり、直接的に使わなくてもより良いソースコードが書けるようになるのではないかなと思います。
私自身もこれから読む章たちは楽しみです。
どんな学びがあるのか、そしてどんな綺麗なソースコードが書けるようになるのか。
私の冒険はまだまだ続きます。