We use AsRef<str> all the time for convenience. It reduces the syntax required on the caller side, when the function only cares about being able to read the value as a string.
This is how I think about the following contracts:
&str: Accept only a borrowed string, either from owned (&String) or static (&'static str).
AsRef<str>: Accept any value that can behave like a borrowed string (&str).
String: Accept only an owned String from runtime.
Cow<str>: Accept either a reference to a string that may be cloned, or an owned string. If it is a reference, it may be cloned deterministically, if it is owned, it will be mutated and moved somewhere else.
Please correct me, I'm sure my understanding is rather flawed.
But what I'm here to discuss is the contrapositive of AsRef<str>. If it means "take any type that can act as a reference to a string value", what is the trait bound for "take any type that can be coerced into an owned String, cloning if necessary, moving if not"?
This sounds like I'm asking for Cow<str>, but I don't believe this is the case. Cow is harder to use. The caller must wrap their value in this type, and pass that to a function. What if a function wants to operate on a type similar to Cow, but it requires ownership in all circumstances? The caller can choose to provide owned data, or it can be a reference to something that can be cloned into a String.
Here is the behavior that I have in mind for several potential types of input:
&str: Clone the value behind the reference into String.
String: Take ownership of the value.
Cow<str>: If it is borrowed, clone the value. If it is owned, make the cow poop it out.
There are several traits that fit the desired contracts:
Into<String>: This may be too lose, but generally as I understand this trait implies a 1:1 map between the original from type and the String, meaning that no information is lost, it is a different representation. This does seem like the best option to me.
ToString: This probably isn't what we want. It could be, but only if T: ToString + !Display, because we probably don't want to be mutating the (lossy) representation that is usually returned from the blanket impl that comes with Display. Display is intended for just that: display to a human. It is not directly a conversion, it is a summary.
ToOwned<String>: I don't know what to think about this one. The name of the trait seems to be an exact description of how I would like the generic to behave. If the value is already owned, using .to_owned() does nothing and there is no wasted work. If the value is a reference, ToOwned implementation handles the behavior, which is usually to delegate to Clone.
What do you all think? How can I make passing an owned String, with as loose a contract as possible, convenient to do?
Edit: Also GenericCow<str> implies Borrow<str>, which is stricter than AsRef<str>. Thus GenericCow is the corresponding match for Borrow instead of AsRef.
Edit #2: On the other hand, Cow<'_, str> and GenericCow<str> are more generic as they support borrowing while you only wanted to require ownership in all cases. Thus Cow/GenericCow are different concepts: 1. borrowing (where Eq, Ord, and Hash behave consistent) instead of cheap conversion (where Eq, Ord, and Hash do not have to behave consistent), and 2. supporting both directions (either to a reference or to an owned value, while you only want an owned value).
This is sometimes more lax, sometimes more strict (I think). More lax because It will accept everything with a string representation due to impl<T: ?Sized> ToString for T where T: Display + ?Sized, see implementators of ToString. More strict because it will not accept anythingeverything that is Into<String>, right?