Borrow y AsRef

Lost traits Borrow and AsRef son muy similares, pero diferentes. He aquí un breve repaso acerca de lo que significan dichos traits:

Borrow

El trait Borrow se usa cuando estamos escribiendo un estructura de datos, y deseamos usar un tipo owned o un tipo borrowed como sinónimo por alguna razón.

Por ejemplo, HashMap posee un metodo get que usa Borrow:

fn main() { fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq }
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where K: Borrow<Q>,
          Q: Hash + Eq

Esta firma es complicada. El parametro K es en lo que estamos interesados ahora. Se refiere a un parametro del HashMap en si mismo:

fn main() { struct HashMap<K, V, S = RandomState> { }
struct HashMap<K, V, S = RandomState> {

El parametro K es el tipo de la clave que usa el HashMap. Al ver de nuevo la firma de get(), solo podemos usar get() cuando la clave implementa Borrow<Q>. De esta manera, podemos crear un HashMap que use claves String pero al momento de buscar use &strs:

fn main() { use std::collections::HashMap; let mut map = HashMap::new(); map.insert("Foo".to_string(), 42); assert_eq!(map.get("Foo"), Some(&42)); }
use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("Foo".to_string(), 42);

assert_eq!(map.get("Foo"), Some(&42));

Esto es debido a que la biblioteca estándar posee una impl Borrow<str> for String.

Para la mayoría de los tipos, cuando desees tomar un tipo ya sea owned o borrowed, un &T es suficiente. Pero un area en la cual Borrow es efectivo es cuando hay mas de un tipo de valor borrowed. Esto es especialmente cierto en referencias y slices: puedes tener ambos un &T un &mut T. Si queremos aceptar ambos tipos Borrow es lo que necesitamos:

fn main() { use std::borrow::Borrow; use std::fmt::Display; fn foo<T: Borrow<i32> + Display>(a: T) { println!("a es un borrowed: {}", a); } let mut i = 5; foo(&i); foo(&mut i); }
use std::borrow::Borrow;
use std::fmt::Display;

fn foo<T: Borrow<i32> + Display>(a: T) {
    println!("a es un borrowed: {}", a);
}

let mut i = 5;

foo(&i);
foo(&mut i);

This will print out a es un borrowed: 5 dos veces.

AsRef

El trait AsRef es un trait de conversion. Es usado para convertir algún valor a una referencia en código generico. Como esto:

fn main() { let s = "Hola".to_string(); fn foo<T: AsRef<str>>(s: T) { let slice = s.as_ref(); } }
let s = "Hola".to_string();

fn foo<T: AsRef<str>>(s: T) {
    let slice = s.as_ref();
}

Cual debería usar?

Podemos ver como ambos son una especie de lo mismo: ambos lidian con las versiones owned y borrowed de algún tipo. Sin embargo, son diferentes.

Escoge Borrow cuando quieras abstraerte por encima de diferentes tipos de borrowing, o cuando estas construyendo una estructura de datos que trata los valores owned y borrowed de formas equivalentes, como el hashing y la comparación.

Usa AsRef cuando desees convertir algo directamente en una referencia, y estés escribiendo código genérico.