% Типажи Borrow и AsRef
Типажи Borrow и AsRef очень похожи, но в то же время
отличаются. Ниже приводится небольшая памятка об этих двух типажах.
Типаж Borrow
Типаж Borrow используется, когда вы пишете структуру данных и хотите
использовать владение и заимствование типа как синонимы.
Например, HashMap имеет метод get, который использует
Borrow:
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Hash + Eq
Эта сигнатура является довольно сложной. Параметр K — это то, что нас здесь
интересует. Он ссылается на параметр самого HashMap:
struct HashMap<K, V, S = RandomState> {
Параметр K представляет собой тип ключа, который использует HashMap.
Взглянем на сигнатуру get() еще раз. Использовать get() возможно, когда ключ
реализует Borrow<Q>. Таким образом, мы можем сделать HashMap, который
использует ключи String, но использовать &str, когда мы выполняем поиск:
#![allow(unused)] fn main() { use std::collections::HashMap; let mut map = HashMap::new(); map.insert("Foo".to_string(), 42); assert_eq!(map.get("Foo"), Some(&42)); }
Это возможно, так как стандартная библиотека содержит impl Borrow<str> for String.
Для большинства типов, когда вы хотите получить право собственности или
позаимствовать значений, достаточно использовать просто &T. Borrow же
становится полезен, когда есть более одного вида занимаемого значения. Это
особенно верно для ссылок и срезов: у вас может быть как &T, так и &mut T.
Если мы хотим принимать оба этих типа, Borrow как раз для этого подходит:
#![allow(unused)] fn main() { use std::borrow::Borrow; use std::fmt::Display; fn foo<T: Borrow<i32> + Display>(a: T) { println!("a заимствовано: {}", a); } let mut i = 5; foo(&i); foo(&mut i); }
Это выведет a заимствовано: 5 дважды.
Типаж AsRef
Типаж AsRef является преобразующим типажом. Он используется в обобщённом коде
для преобразования некоторого значения в ссылку. Например:
#![allow(unused)] fn main() { let s = "Hello".to_string(); fn foo<T: AsRef<str>>(s: T) { let slice = s.as_ref(); } }
Что в каком случае следует использовать?
Мы видим, что они вроде одинаковы: имеют дело с владением и заимствованием значения некоторого типа. Тем не менее, эти типажи немного отличаются.
Используйте Borrow, когда вы хотите абстрагироваться от различных видов
заимствований, или когда вы строите структуру данных, которая использует
владеющие и заимствованные значения как эквивалентные. Например, это может
пригодиться в хэшировании и сравнении.
Используйте AsRef, когда вы пишете обобщённый код и хотите непосредственно
преобразовать что-либо в ссылку.