ruk·si

🦀 Rust
🐮 Bovine Ergonomics

Updated at 2024-02-24 02:11

If you want to make more ergonomic programming interface, you can use cows.

Cow keeps the user guessing if the value is to be consumed. 😋🥩

use std::borrow::Cow;

fn do_it<'a>(text: impl Into<Cow<'a, str>>) {
    match text.into() {
        Cow::Borrowed(s) => println!("borrowed: {}", s),
        Cow::Owned(s) => println!("owned: {}", s),
    }
}

// if you return cows, Rust can more easily figure out the lifetime
fn lifetime_from_parameter(input: &str) -> Cow<str> {
    Cow::Borrowed(&input[0..1])
}

fn main() {
    let txt = "Hello, world!".to_string();

    // text: impl Into<Cow<'a, str>>
    do_it("Hello, world!");
    do_it(&txt);
    do_it(txt);
    let xxx = lifetime_from_parameter("lol");
    dbg!(xxx);

    // text: &str
    //do_it("Hello, world!");
    //do_it(&txt);
    //do_it(txt.as_ref());

    // text: impl Into<&'a str>
    //do_it("Hello, world!");

    // text: impl AsRef<str> + std::fmt::Display
    //do_it("Hello, world!");
    //do_it(&txt);
    //do_it(txt);
}

How you would use an API like this:

  • if you don't need the variable after the call, just pass the value
  • if you need the variable after the call, pass a reference

This can make the API nicer to use, especially if you are not returning Cows but just taking Into<Cow>s. Can lead to more noisy internals though.

But, it does make the API less prone to breaking changes, and more open for future optimization without user modifications.

The original use-case is optimization ⚡️ For example, take in a string and a bovine string that might have matching content to replace or not. If there is a match, the bovine string is mutated and Cow::Owned returned but if not, just the Cow::Borrowed is returned without any extra allocation happening.

Copy-on-Write - Cow

Normally you would use Cow<T> if:

  • T is expensive to clone and want to avoid it at all costs.
  • Receiver sometimes wants to mutate the T but not always.
  • Caller sometimes wants access to the T after the call, but not always.