C ++には、値、左辺値参照、右辺値参照の3つの方法でパラメーターを関数に渡す方法があります。これらのうち、値渡しは、呼び出された関数が独自のコピーを受け取るという意味で所有権を作成し、右辺値参照渡しは、値が消費される可能性がある、つまり、呼び出し元によって使用されなくなることを示します。左辺値参照による受け渡しは、オブジェクトが呼び出し元から一時的に借用されることを意味します。
ただし、これらは「慣例により」である傾向があり、コンパイラが常にチェックできるとは限りません。また、を使用して、誤って左辺値参照を右辺値参照に変換できますstd::move()
。具体的には、3つの問題があります。
参照は、参照するオブジェクトよりも長く存続できます。Rustのライフタイムシステムはこれを防ぎます。
いつでも複数の可変/非定数参照をアクティブにすることができます。Rustのボローチェッカーはこれを防ぎます。
参照をオプトアウトすることはできません。呼び出された関数のシグネチャを知らないと、その関数がオブジェクトへの参照を作成するかどうかを呼び出しサイトで確認できません。したがって、クラスの特別なメソッドを削除したり、「参照なし」スタイルガイドへの準拠について呼び出しサイトを監査したりしても、参照を確実に防ぐことはできません。
ライフタイムの問題は、基本的なメモリの安全性に関するものです。参照されるオブジェクトの有効期限が切れたときに参照を使用することはもちろん違法です。しかし、オブジェクト内に参照を保存するとき、特にそのオブジェクトが現在のスコープを超えているときは、ライフタイムを忘れがちです。C ++型システムは、オブジェクトの有効期間をまったくモデル化しないため、これを考慮できません。
std::weak_ptr
スマートポインタは、プレーン、参照に似たエンコードの所有権の意味を行いますが、参照されたオブジェクトを介して管理されている必要がありshared_ptr
、つまりは、参照カウントです。これはゼロコストの抽象化ではありません。
C ++にはconstシステムがありますが、これはオブジェクトを変更できるかどうかを追跡するのではなく、その特定の参照を通じてオブジェクトを変更できるかどうかを追跡します。それは「大胆不敵な並行性」に十分な保証を提供しません。対照的に、Rustは、唯一の参照であるアクティブな可変参照(「このオブジェクトを変更できる唯一のユーザー」)が存在し、不変の参照がある場合、そのオブジェクトへのすべての参照が不変であることを保証します(「オブジェクトから読み取ることはできますが、変更することはできません」)。
C ++では、mutexを使用したスマートポインターを介したオブジェクトへのアクセスを保護したくなるかもしれません。しかし、上記で説明したように、参照を取得すると、予想される存続期間を回避できます。したがって、このようなスマートポインターは、管理対象オブジェクトへの単一のアクセスポイントであることを保証できません。ほとんどのプログラマーは自分自身を妨害したくないので、そのようなスキームは実際に機能するかもしれませんが、型システムの観点からはこれはまだ完全に不健全です。
スマートポインターの一般的な問題は、それらがコア言語の上にあるライブラリであるということです。コア言語機能のセットにより、これらのスマートポインターが有効になります。たとえば、std::unique_ptr
ムーブコンストラクターが必要です。ただし、コア言語内の欠陥を修正することはできません。関数を呼び出すときに暗黙的に参照を作成し、参照をダングリングする機能は、コアC ++言語が不適切であることを意味します。可変参照を単一の参照に制限できないということは、C ++があらゆる種類の並行性を持つ競合状態に対する安全性を保証できないことを意味します。
もちろん、多くの点で、C ++とRustは、特に静的に決定されたオブジェクトライフタイムの概念に関して、嫌いというよりも似ています。しかし、正しいC ++プログラムを書くことは可能ですが(プログラマーが間違いを犯さなければ)、Rust は議論されたプロパティに関する正確さを保証します。