Ответ Trentcl дает вам решение вашей реальной проблемы, полагаясь только на реализацию AsRef<str>
. Следующее - это скорее ответ на более общий вопрос из вашего заголовка.
Определенные черты несут с собой инварианты, которые реализации должны обеспечивать. В частности, если реализации Borrow<T>
также реализуют Eq
, Hash
и Ord
, то реализации этих трейтов для T
должны вести себя идентично. Это требование является способом сказать, что заимствованное значение "то же самое" , что и исходное значение, но просто рассматривается по-другому. Например, реализация String: Borrow<str>
должна возвращать весь фрагмент строки; было бы неправильно возвращать подслой.
AsRef
не имеет этого ограничения. Реализация AsRef<T>
может реализовать такие черты, как Hash
и Eq
, совершенно иначе, чем T
. Если вам нужно вернуть ссылку только на часть структуры, тогда AsRef
может это сделать, а Borrow
- нет.
Все это означает, что вы не можете получить действительную реализацию Borrow<T>
из произвольная реализация AsRef<T>
: реализация AsRef
может не обеспечивать соблюдение инвариантов, которые требуются Borrow
.
Однако работает наоборот. Вы можете создать реализацию AsRef<T>
с произвольным Borrow<T>
:
use std::borrow::Borrow;
use std::convert::AsRef;
use std::marker::PhantomData;
pub struct BorrowAsRef<'a, T: ?Sized, U: ?Sized>(&'a T, PhantomData<U>);
impl<'a, T, U> AsRef<U> for BorrowAsRef<'a, T, U>
where
T: Borrow<U> + ?Sized,
U: ?Sized,
{
fn as_ref(&self) -> &U {
self.0.borrow()
}
}
pub trait ToAsRef<T: ?Sized, U: ?Sized> {
fn to_as_ref(&self) -> BorrowAsRef<'_, T, U>;
}
impl<T, U> ToAsRef<T, U> for T
where
T: ?Sized + Borrow<U>,
U: ?Sized,
{
fn to_as_ref(&self) -> BorrowAsRef<'_, T, U> {
BorrowAsRef(self, PhantomData)
}
}
fn borrowed(v: &impl Borrow<str>) {
needs_as_ref(&v.to_as_ref())
}
fn needs_as_ref(v: &impl AsRef<str>) {
println!("as_ref: {:?}", v.as_ref())
}
Почему Join
реализован для типов, реализующих Borrow
, но не AsRef
?
Это общая реализация для всех типов, которые реализовать Borrow<str>
, что означает, что он также не может быть реализован для типов, реализующих AsRef<str>
. Даже с включенной нестабильной функцией min_specialization
это не сработает, потому что реализация AsRef
не более "специфическая c", чем реализация Borrow
. Поэтому им пришлось выбрать один или другой.
Можно было бы возразить, что AsRef
был бы лучшим выбором, потому что он охватывает больше типов. Но, к сожалению, я не думаю, что сейчас это можно изменить, потому что это было бы критическим изменением.