Borrow
, BorrowMut
, ToOwned
これらはスライス型を独自に定義するうえで特に使い勝手に影響するトレイトである。
具体的には、 .to_owned()
が使えるようになる1のと、 std::borrow::Cow
が使えるようになる。
#![allow(unused)] fn main() { // 所有権なしのスライス型と所有権付きの型を相互に紐付ける。 #[repr(transparent)] pub struct MyStr(str); impl MyStr { fn as_str(&self) -> &str { unimplemented!() } } pub struct MyString(String); impl MyString { fn as_my_str(&self) -> &MyStr { unimplemented!() } fn as_my_str_mut(&mut self) -> &mut MyStr { unimplemented!() } fn new(s: String) -> Self { unimplemented!() } } impl core::borrow::Borrow<MyStr> for MyString { #[inline] fn borrow(&self) -> &MyStr { self.as_my_str() } } impl core::borrow::BorrowMut<MyStr> for MyString { #[inline] fn borrow_mut(&mut self) -> &mut MyStr { self.as_my_str_mut() } } impl std::borrow::ToOwned for MyStr { type Owned = MyString; fn to_owned(&self) -> Self::Owned { MyString::new(self.as_str().to_owned()) } } }
Borrow<T>
は T
型の参照を取り出すためのトレイトである。
BorrowMut<T>
は mutable 参照を取り出す版。
ToOwned
は、参照型から所有権付きの型 (と値) を得るためのトレイトである。
Clone
トレイトが実装されてさえいれば、 &T
の所有権付きの型 T
の値を .clone()
によって得られる。
しかし [T]
、 str
や MyStr
などのスライス型は DST であり Clone
トレイトを実装できず、参照を外した型の値をそのまま保持できないから、代わりに何かしらのバッファ的な型が必要になる。
これは [T]
の場合は Vec<T>
であり、 str
の場合は String
であり、同様に MyStr
に対しては MyString
を用意してやろうということである。
同じようなコードになるが、一応 unsafe を使うことになるので AsciiStr
のコード例も確認しておこう。
#![allow(unused)] fn main() { // ほとんど同じだが unsafe を使っている部分がある。 #[repr(transparent)] pub struct AsciiStr(str); impl AsciiStr { fn as_str(&self) -> &str { unimplemented!() } } pub struct AsciiString(String); impl AsciiString { fn as_ascii_str(&self) -> &AsciiStr { unimplemented!() } fn as_ascii_str_mut(&mut self) -> &mut AsciiStr { unimplemented!() } unsafe fn new_unchecked(s: String) -> Self { unimplemented!() } } impl core::borrow::Borrow<AsciiStr> for AsciiString { #[inline] fn borrow(&self) -> &AsciiStr { self.as_ascii_str() } } impl core::borrow::BorrowMut<AsciiStr> for AsciiString { #[inline] fn borrow_mut(&mut self) -> &mut AsciiStr { self.as_ascii_str_mut() } } impl std::borrow::ToOwned for AsciiStr { type Owned = AsciiString; fn to_owned(&self) -> Self::Owned { let s = self.as_str(); unsafe { // SAFETY: Valid `AsciiStr` string is also valid as `AsciiString`. AsciiString::new_unchecked(s.to_owned()) } } } }
&AsciiStr
から直接 AsciiString
を作れないため &str
と String
を経由する。
これ自体は MyStr
の例と同じだが、今回の例では String
から AsciiString
を作るのが unsafe
な操作である。
不変条件を満たしていること自体は明らかなので、コメントで明示したうえで素直に書けばよい。
実は "hello".to_owned()
などとした場合、 <str as ToOwned>::to_owned("hello")
が呼ばれている。
存在感の薄いトレイトだが、 ToOwned
は prelude に入っているのである。