Contents

COW ๐Ÿ„ in Rust ๐Ÿฆ€

clone-on-write

dfinity/ic ์ฝ”๋“œ๋ฅผ ๋ณด๋‹ค ์ดํ•ด๋˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋“ค์ด ์žˆ์–ด, Cow๋ฅผ ์™œ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ •๋ฆฌํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค.

TL;DR

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),
}
  • Cow์˜ ์•ฝ์–ด ๋œป์€ clone-on-write๋กœ read๊ฐ€ ์•„๋‹Œ write์‹œ clone์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • Cow๋Š” Borrowed, Owned๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” enum ํƒ€์ž…์ž…๋‹ˆ๋‹ค.

  • ์ œ๋„ˆ๋ฆญ B๋Š” ‘a์™€, ToOwned, ?Sized๋กœ ๋ฐ”์šด๋“œ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

  • ToOwned trait

    • borrowed ๋ฐ์ดํ„ฐ์˜ Clone์— ๋Œ€ํ•œ ์ผ๋ฐ˜ํ™”
    • clone์‹œ์ผœ์„œ, Owned ํƒ€์ž…์„ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • ?Sized trait

    • ์ปดํŒŒ์ผ ํƒ€์ž„์— constant size๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…
    • ๋ชจ๋“  ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” implicit bound๋กœ Sized๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋‹ค.
    • ?๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด bound๋ฅผ remove์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ Cow๋Š” Borrowed ๋˜๋Š” Owned ๋‘˜ ๋ชจ๋‘๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด &str, Stringํƒ€์ž… ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๊ธธ ์›ํ•˜๋Š” ๊ฒฝ์šฐ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#[derive(CandidType, Deserialize, Clone)]
struct LogoResult {
    logo_type: Cow<'static, str>,
    data: Cow<'static, str>,
}

// ...
const DEFAULT_LOGO: LogoResult = LogoResult {
    data: Cow::Borrowed(include_base64!("logo.png")), //&str
    logo_type: Cow::Borrowed("image/png"), //&str
};

Cow๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

6 thing you can do with the ๐Ÿ„ in ๐Ÿฆ€

1. A function rarely modifying the data

๋ถˆํ•„์š”ํ•˜๊ฒŒ clone์„ ํ•˜๊ฒŒ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ cow๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. FYI to_string()์€ ๋ณต์‚ฌ๋ณธ์„ ์ „๋‹ฌํ•œ๋‹ค.

before

1
2
3
4
5
6
7
8
fn remove_whitespaces(s: &str) -> String {
    s.to_string().replace(' ', "")
}

fn main() {
    let value = remove_whitespaces("Hello world!");
    println!("{}", value);
}

after

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::borrow::Cow;

fn remove_whitespaces(s: &str) -> Cow<str> {
    if s.contains(' ') {
        Cow::Owned(s.to_string().replace(' ', ""))
    } else {
        Cow::Borrowed(s)
    }
}

fn main() {
    let value = remove_whitespaces("Hello world!");
    println!("{}", value);
}

2. A struct optionally owning the data

3. A clone on write struct

4. Keep your own type inside it

5. Borrow the type as dyn Trait

6. Implement safe wrapper over FFI type