Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ジェネリクスとunsafe

unsafeコードと相互作用するジェネリック型には注意してください。ジェネリック型が、その契約を指定するunsafeトレイトによって境界付けられていない限り、ジェネリック型の結果が信頼できる、または正しいとは期待できません。

これがよく問題になる場所の1つが、RangeBoundsトレイトです。RangeBoundsの実装から与えられる開始境界と終了境界は、共有参照を介して動作するため同じままである、と仮定するかもしれません。しかし、必ずしもそうとは限らず、敵対的な実装は呼び出しの間に境界を変更する可能性があります。

struct EvilRange(Cell<bool>);

impl RangeBounds<usize> for EvilRange {
    fn start_bound(&self) -> Bound<&usize> {
        Bound::Included(if self.0.get() {
            &1
        } else {
            self.0.set(true);
            &0
        })
    }
    fn end_bound(&self) -> Bound<&usize> {
        Bound::Unbounded
    }
}

これは過去に、境界が同じままであることをアサートせずに境界に基づいて安全性の仮定を行うコードで問題を引き起こしました

unsafeと相互作用するためにジェネリック型を使用するコードは、まずそれらを既知の型に変換し、それからジェネリックの代わりにその型を扱うようにするべきです。RangeBoundsの例では、これは具象的なRange、または(Bound, Bound)のタプルに変換することを意味するかもしれません。

レビュー担当者向け

unsafeブロックも含むジェネリック関数に注意し、それらのジェネリクスの敵対的な実装がどのように安全性を侵害し得るかを検討してください。