この記事について
個人的なメモです.
The Rust Programming Language 日本語版
を読んで学んだこと,気付いたことを追記していきます.
安全な理由
- 所有権のおかげで、メモリの複数回解放(memory corruption)が起こらない
-
参照のおかげで、データ競合が起こらない
- 同一スコープで、ある変数の参照を複数生成する場合、全てが不変参照でなければならない?
-
スライスのおかげで、同期をとる変数を減らせる
- e.g.) ある文字列の部分文字列を扱う場合に、配列のインデックスを変数に束縛するのではなく、スライスを束縛することでコンパイラが事前に危険なコードを検知してくれる?
- ゼロコスト抽象化?
- 静的ディスパッチ、人間による横展開と同じ?
- ゼロコスト抽象化とは何なのか? - Qiita
-
null機能がない
null値の問題は、nullの値をnullでない値のように使用しようとしたら、何らかの種類のエラーが出ることです。 このnullかそうでないかという特性は広く存在するので、この種の間違いを大変犯しやすいのです。
しかしながら、nullが表現しようとしている概念は、それでも役に立つものです: nullは、 何らかの理由で現在無効、または存在しない値のことなのです。
-
orphan rule
トレイト実装で注意すべき制限の1つは、トレイトか対象の型が自分のクレートに固有(local)である時のみ、 型に対してトレイトを実装できるということです。例えば、Displayのような標準ライブラリのトレイトをaggregatorクレートの機能の一部として、 Tweetのような独自の型に実装できます。型Tweetがaggregatorクレートに固有だからです。 また、SummaryをaggregatorクレートでVecに対して実装することもできます。 トレイトSummaryは、aggregatorクレートに固有だからです。
しかし、外部のトレイトを外部の型に対して実装することはできません。例として、 aggregatorクレート内でVecに対してDisplayトレイトを実装することはできません。 DisplayとVecは標準ライブラリで定義され、aggregatorクレートに固有ではないからです。 この制限は、コヒーレンス(coherence)、特に孤児のルール(orphan rule)と呼ばれるプログラムの特性の一部で、 親の型が存在しないためにそう命名されました。この規則により、他の人のコードが自分のコードを壊したり、 その逆が起きないことを保証してくれます。この規則がなければ、2つのクレートが同じ型に対して同じトレイトを実装できてしまい、 コンパイラはどちらの実装を使うべきかわからなくなってしまうでしょう。
速い理由
- 明示しないかぎり deep copy が起こらず、 shallow copy のみが行われる
- スタックに積まれるようなスカラー値では、 sahllow copy == deep copy
-
単相化(monomorphization)
ジェネリックな型引数を使用すると、実行時にコストが発生するのかな、と思うかもしれません。 嬉しいことにRustでは、ジェネリクスを、具体的な型があるコードよりもジェネリックな型を使用したコードを実行するのが遅くならないように実装しています。
コンパイラはこれを、ジェネリクスを使用しているコードの単相化をコンパイル時に行うことで達成しています。 単相化(monomorphization)は、コンパイル時に使用されている具体的な型を入れることで、 ジェネリックなコードを特定のコードに変換する過程のことです。
この過程において、コンパイラは、リスト10-5でジェネリックな関数を生成するために使用した手順と真逆のことをしています: コンパイラは、ジェネリックなコードが呼び出されている箇所全部を見て、 ジェネリックなコードが呼び出されている具体的な型のコードを生成するのです。
チュートリアルのメモ
8.3
練習問題をやる!!
9.2
if error.kind() == ErrorKind::Notfoundという条件式は、マッチガードと呼ばれます: アームのパターンをさらに洗練するmatchアーム上のおまけの条件式です。この条件式は、 そのアームのコードが実行されるには真でなければいけないのです; そうでなければ、 パターンマッチングは継続し、matchの次のアームを考慮します。パターンのrefは、 errorがガード条件式にムーブされないように必要ですが、ただ単にガード式に参照されます。 refを使用して&の代わりにパターン内で参照を作っている理由は、第18章で詳しく講義します。 手短に言えば、パターンの文脈において、&は参照にマッチし、その値を返しますが、 refは値にマッチし、それへの参照を返すということなのです。
9.3
関数にはしばしば契約が伴います: 入力が特定の条件を満たすときのみ、振る舞いが保証されるのです。 契約が侵されたときにパニックすることは、道理が通っています。なぜなら、契約侵害は常に呼び出し側のバグを示唆し、 呼び出し側に明示的に処理してもらう必要のある種類のエラーではないからです。実際に、 呼び出し側が回復する合理的な手段はありません; 呼び出し側のプログラマがコードを修正する必要があるのです。
10.2
pub fn notify(item1: &impl Summary, item2: &impl Summary) {
この関数が受け取るitem1とitem2の型が(どちらもSummaryを実装する限り)異なっても良いとするならば、impl Traitは適切でしょう。 両方の引数が同じ型であることを強制することは、以下のようにトレイト境界を使ってのみ表現可能です:
pub fn notify<T: Summary>(item1: &T, item2: &T) {
引数であるitem1とitem2の型としてジェネリックな型Tを指定しました。 これにより、item1とitem2として関数に渡される値の具体的な型が同一でなければならない、という制約を与えています。