아카이브
Cow
Cow
개요
우리는 어떤 값이 참조인가, 아니면 소유권을 가지고 있는가에 대해 코드상으로 알고 싶을 때가 있습니다.
std::borrow에 존재하는 ToOwned라는 트레잇이 존재합니다.
ToOwned는 소유권이 있는 (owned) 타입으로 변환할 수 있는 트레잇입니다.
예를 들어, to_owned 함수를 사용하여, 참조 &str를 소유권이 있는 String으로 변환할 수 있습니다.
이를 이용해서 구현하면 좋을 듯한데, 이미 구현된 게 있으니: 바로 Cow (Copy On Write) 열거형입니다.
Copy On Write는 읽기만 필요한 경우, 굳이 대상을 다시 쓸 필요가 없으며, 수정이 있다면 그 대상을 새로 만드는 리소스 관리 기법입니다. (이 때문에 크기가 커질 수 있습니다.)즉,
Cow<T>는 읽기 전용입니다.
Cow<T> 열거형의 구현은 다음과 같습니다:
1
2
3
4
5
6
7
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은 다음과 같이 구현되어 있습니다:
1
2
3
4
5
6
7
8
9
10
11
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에 포함됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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) 값인가에 대해 알 수 있습니다.