Cow 타입
우리는 어떤 값이 참조인가, 아니면 소유권을 가지고 있는가에 대해 코드상으로 알고 싶을 때가 있습니다.
std::borrow
에 존재하는 ToOwned
라는 트레잇이 존재합니다.
ToOwned
는 소유권이 있는 (owned
) 타입으로 변환할 수 있는 트레잇입니다.
예를 들어, to_owned
함수를 사용하여, 참조 &str
를 소유권이 있는 String
으로 변환할 수 있습니다.
이를 이용해서 구현하면 좋을 듯한데, 이미 구현된 게 있으니: 바로 Cow
Copy On Write
는 읽기만 필요한 경우, 굳이 대상을 다시 쓸 필요가 없으며, 수정이 있다면 그 대상을 새로 만드는 리소스 관리 기법입니다. (이 때문에 크기가 커질 수 있습니다.)즉,
Cow<T>
는 읽기 전용입니다.
Cow<T>
열거형의 구현은 다음과 같습니다:
#![allow(unused)] fn main() { pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized, { Borrowed(&'a B), Owned(<B as ToOwned>::Owned), } }
제네릭 B
는 수명 'a
, ToOwned
와 ?Sized
로 바운드되어 있습니다.
B
가 크기를 알 수 있는 타입인지 아닌지 모르니,?Sized
가 포함되었습니다.
예를 들어봅시다. Borrowed
엔 "Hello, World!"
, &'static str
가 포함될 수 있습니다.
반면 String
은 Owned
에 포함됩니다. 그 이유는, ToOwned
트레잇에 대해 &str
은 다음과 같이 구현되어 있습니다:
#![allow(unused)] fn main() { impl ToOwned for str { type Owned = String; fn to_owned(&self) -> String { unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } } fn clone_into(&self, target: &mut String) { // ... } } }
연관 타입(associated type
) Owned
가 String
으로 명시되어 있습니다.
즉, String
은 B
(&'static str
)의 Owned
가 String
이기 때문에, String
은 Owned
에 포함됩니다.
use std::borrow::Cow; fn foo(x: &str) -> Cow<'static, str> { if x == "foo" { Cow::Borrowed("bar") } else { Cow::Owned(x.to_string()) } } fn main() { match foo("foo") { Cow::Borrowed(x /* &str */) => println!("Borrowed: {x}"), Cow::Owned(x /* String */) => println!("Owned: {x}"), } match foo("baz") { Cow::Borrowed(x /* &str */) => println!("Borrowed: {x}"), Cow::Owned(x /* String */) => println!("Owned: {x}"), } }
이런 방법으로, 위에서 서술한 참조인가, 아니면 소유권을 가지고 있는 (owned
) 값인가에 대해 알 수 있습니다.