Cow

Cow es un tipo de enumerado que significa "clone on write" (clonq en escritura). Permite devolver una referencia &str si no se necesita un String. Y devuelve un String, cuando se necesita. Lo mismo sucede para cualquier otro tipo, como arrays vs. Vec, etc.

Para entenderlo, se puede observar su definición:

pub enum Cow<'a, B>
where
    B: 'a + ToOwned + ?Sized,
 {
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

fn main() {}

En el momento en que se aparece una indicación de tiempo de vida como 'a, se tiene conocimiento de que este tipo funciona con referencias, ya que, como se ha visto, es para lo que tienen sentido las indicaciones de tiempo de vida.

El rasgo ToOwned significa que B debe ser un tipo que puede convertirse en un tipo "adueñado", que tiene "una variable propietario". Por ejemplo, str que normalmente se usa a través de préstamo por referencia, se puede convertir en una String que tendrá una variable propietario.

Lo siguiente que aparece es ?Sized, que significa que "puede ser Sized o no". Casi todos los tipos de Rust son Sized, pero hay algunos, como str, que no. Es el motivo por el que este tipo se debe usar siempre por referencia, porque el compilador no conoce su tamaño. Por ello, si se quiere que una definición use tipos como str, resulta necesario que admita ?Sized.

A continuación, el código presenta las variantes del enumerado. Que son Borrowed y Owned.

Por ejemplo, si se imagina una función que devolviera Cow<'static, str>. Si a esa función se le dice que devuelva "Mi mensaje".into(), Rust mirará el tipo de "Mi mensaje", que es un str. Se trata de un tipo Borrowed, por lo que el tipo seleccionado del enumerado será Borrowed(&'a, B) y el tipo concreto que se devuelve es Cow::Borrowed(&'static str).

Si en vez de lo anterior, se le pidiera devolver format!("{}", "Mi mensaje").into(), entonces el tipo es String, porque format! crea String. Y el tipo enumerado seleccionado será Owned.

A continuación se presenta un ejemplo para probar Cow. Se pasará un número a una función que devuelve Cow<'static, str>. Dependiendo del número, se crear un &str o una String. Después, se usa .into() para convertirlo en un Cow. De esta forma, Rust seleccionará Cow::Borrowed o Cow::Owned. Se usa un match al finalizar, para ver qué se ha seleccionado:

use std::borrow::Cow;

fn modulo_3(input: u8) -> Cow<'static, str> {
    match input % 3 {
        0 => "El resto es 0".into(),
        1 => "El resto es 1".into(),
        remainder => format!("El resto es {}", remainder).into(),
    }
}

fn main() {
    for number in 1..=6 {
        match modulo_3(number) {
            Cow::Borrowed(message) => println!("{}, es el valor que se pasa. El Cow es prestado en este mensaje: {}", number, message),
            Cow::Owned(message) => println!("{}, es el valor que se pasa. El Cow es propietario en este mensaje: {}", number, message),
        }
    }
}

Que imprime:

1, es el valor que se pasa. El Cow es prestado en este mensaje: El resto es 1
2, es el valor que se pasa. El Cow es propietario en este mensaje: El resto es 2
3, es el valor que se pasa. El Cow es prestado en este mensaje: El resto es 0
4, es el valor que se pasa. El Cow es prestado en este mensaje: El resto es 1
5, es el valor que se pasa. El Cow es propietario en este mensaje: El resto es 2
6, es el valor que se pasa. El Cow es prestado en este mensaje: El resto es 0

Cow tiene otros métodos como into_ownedo into_borrowed para cambiar el tipo manualmente si resulta necesario.