I want to create a function that can take as an argument String
, &str
, Rc<String>
, Arc<String>
, etc. String
is just an example, the parameter can be of any type. I can do this using AsRef
:
fn is_empty_as_ref<T: AsRef<str>>(arg1: T) -> bool {
arg1.as_ref().is_empty()
}
Or Deref
:
fn is_empty_deref<T: Deref<Target = str>>(arg1: T) -> bool {
arg1.is_empty()
}
In my playground, both of these approaches work well. So I have 3 questions:
String is just an example, the parameter can be of any type.
It can not, actually. Because there is no impl AsRef<T> for T
. So for most types, AsRef
will not let you pass in an owned value.
In general, without any specific case, what trait should I use for such polymorphism?
In general, none of the above, request an &T
and let the caller handle the conversion. Notably if they have a Deref<Target=T>
they can just call
do_thing(&value)
and deref coercion will handle the rest.
In which cases should I use one instead of the other?
I don't believe there's anything clear-cut. The documentation for AsRef
has various notes and furthermore covers the case of Borrow
: https://doc.rust-lang.org/std/convert/trait.AsRef.html
Generally speaking, Deref
is what's implemented by smart pointers, to allow accessing their pointees. "Smart pointer" is itself a somewhat fuzzy concept, usually designated by the ability to be Deref'd, and notably does not mean "transparent wrapper" since e.g. String
or Vec
are smart pointers.
In the stdlib, AsRef
is used to cheaply convert to broader / less restrictive types with the same underlying structure e.g. strings (of all kinds) to OsStr
or Path
, as a String
is more restrictive than the other two, and thus any string is also a valid OsStr / Path. In an extremely limited way, AsRef
acts a bit like an inheritance system.
Finally Borrow
is more specific to maps, in some ways it's a more restrictive than AsRef
as it has additional requirements (specifically that the Borrow-ed reference has hashing, equality, and ordering matching the original), but it is implemented for T
. So Borrow
has a two-way substitutability implication, it considers that the borrowed and the borrowee are equivalent but for ownership, which is not the case of AsRef
.
Are there other commonly used traits for that kind of polymorphism?
What kind of polymorphism? Internally specific but externally generic parameters? From/Into and TryFrom/TryInto are probably the most common.