Deref, DerefMut

DerefDerefMut はある型を透過的に別の型への参照として振る舞わせるもので、非常に便利である一方、 strong typedef の「内部的には同じ型を別個のものとして区別させる」という目的と反するものでもある。 標準ライブラリのドキュメントでは、これらのトレイトはスマートポインタのみに実装すべきであると強調されている。

Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref. On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.

——Rust 1.48.0 の std::ops::Deref のドキュメント。 強調は原文ママ

たとえば str は内部的には [u8] であるが、 Deref<Target=[u8]> for str は実装されていない。 このような「透過的に同一視されてほしいわけではない型」は AsRef<[u8]> だったり .as_bytes() で明示的な変換を行うべしということである。

一方で、 &String が透過的に &str として扱えてほしいとか Vec<u8> を透過的に &[u8] として扱いたいとかは全くもって正当な要求である。 実際、標準ライブラリでも Deref<Target=str> for String とか Deref<Target=[u8]> for Vec<u8> などの実装がされている。 よって、所有権付きの独自型から所有権なしのスライス型への Deref による変換は一般的に実装すべきである。


#![allow(unused)]
fn main() {
// 独自スライス型への暗黙の変換を許容する。

#[repr(transparent)]
pub struct MyStr(str);

#[derive(Clone)]
pub struct MyString(String);
impl MyString {
    fn as_my_str(&self) -> &MyStr { unimplemented!() }
    fn as_my_str_mut(&mut self) -> &mut MyStr { unimplemented!() }
}

impl std::ops::Deref for MyString {
    type Target = MyStr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_my_str()
    }
}

impl std::ops::DerefMut for MyString {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_my_str_mut()
    }
}
}

追加の制約の有無に関係なく同じような実装になるため、 AsciiStringAsciiByteBuf への実装例は省略する。

一応例としてコードは載せるが、特に所有権のないスライス型に本当に Deref を実装すべきかは熟慮すべきである。 困ったら実装せずにおくのがよい。 後から stdcore のトレイトを実装しても breaking change にはならないはずである。


#![allow(unused)]
fn main() {
// 独自スライス型への Deref と DerefMut の実装。

#[repr(transparent)]
pub struct MyStr(str);

// Do you really want this?
impl std::ops::Deref for MyStr {
    type Target = str;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// Do you really REALLY want this?
impl std::ops::DerefMut for MyStr {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
}

再三繰り返すが、追加の制約がある型の場合、くれぐれも内側の型 (MyStr の例であれば str) への mutable 参照を safe に露出させてはいけない。 たとえば AsciiStr から DerefMut 経由で &mut str を露出させるのは厳禁である。

例のごとく、 AsciiStrAsciiBytes への実装例は省略する。