I feel like std
lacks such a trait.
I have brought this up previously on IRLO and previously on URLO (edit: fixed link), with some frustrations caused and leading to some discoveries regarding AsRef
and unfixable FIXMEs in core
.
Note that AsRef
isn't about owning vs borrowing but about type conversion (by reference). Also see the AsRef
documentation, which states:
Used to do a cheap reference-to-reference conversion.
When it comes to borrowing vs owned values (instead of type conversion), the proper traits are in the std::borrow
module: ToOwned
and Borrow
. But these traits lack a method like into_owned
, which is only a method of the Cow
type.
IMHO the correct (zero-cost) abstraction for taking a string as &str
or String
would be something like deref_owned::GenericCow
, which provides an into_owned
method as part of a trait (instead of type):
use deref_owned::GenericCow;
use std::borrow::Borrow;
fn generic_fn(arg: impl GenericCow<Borrowed = str>) {
let reference: &str = &*arg; // or: arg.borrow()
assert_eq!(reference, "Hello");
let owned: String = arg.into_owned();
assert_eq!(owned, "Hello".to_string());
}
(not available on Playground)
This isn't idiomatic, however, and the usual approach is to rather use std::borrow::Cow
and accept the runtime overhead (with the gain of having less trouble with the syntax overhead of generics and only needing std
types/traits).
Update:
Note that apart from being non-idiomatic, the above solution with GenericCow
would require you to wrap a plain String
on the caller side, as shown in this example. So it isn't optimal either. But the same holds when using Cow
, where you also need to write Cow::Owned(owned_string)
instead of just owned_string
as an argument.
If you never need to unwrap the inner String
(i.e. avoiding a clone
in case of an owned String
being passed and needed), you might simply use a Borrow<str>
bound:
use std::borrow::Borrow;
fn foo<T: Borrow<str>>(arg: T) {
println!("Got: {}", arg.borrow());
}
fn main() {
foo("abc".to_string());
foo("abc");
}
(Playground)