If you're writing a method that will take in a string that will be stored as an owned string, but you'll end up having quite a few callers with string literals -- how would you model the function parameter?
Are there others (with any merit) beyond these:
#[derive(Default)]
struct MyStruct {
s: String
}
impl MyStruct {
fn set_owned(&mut self, s: String) {
self.s = s;
}
fn set_ref(&mut self, s: &str) {
self.s = s.to_owned();
}
fn set_asref(&mut self, s: impl AsRef<str>) {
self.s = s.as_ref().to_owned();
}
fn to_str(&mut self, s: impl ToString) {
self.s = s.to_string();
}
}
fn main() {
let mut ms = MyStruct::default();
// Pro: Passing (owned) String is very natural and doesn't allocate
// superflous strings
// Con: Requires explicit conversion at call site for refs and literals
ms.set_owned("hello".into());
let owned = String::from("hello");
ms.set_owned(owned);
// Pro: Passing ref/literals is very natural
// Con: If owned string is of no use, it could have been reused rather than
// allocating a new one.
ms.set_ref("hello");
let owned = String::from("hello");
ms.set_ref(&owned);
// Pro: Ergonomic for both ref/literals and owned
// Con: Adds overhead in callee for owned case
ms.set_asref("hello");
let owned = String::from("hello");
ms.set_asref(owned);
// Pro: Ergonomic for both ref/literals and owned
// Accepts anything that is ToString (which turns out to be a lot)
// Con: Hidden superfluous clone for owned case. Adds overhead in callee
// for owned case.
ms.to_str("hello");
let owned = String::from("hello");
ms.to_str(owned);
ms.to_str(42);
}
I used to use AsRef<str>
a lot a few years ago, but someone pointed out that if you're using AsRef<str>
to create an owned String
inside the function, then you haven't understood ToString
, so I started using that instead. However, once I started using clippy at maximum nag mode, I started getting warnings about impl ToString
(it wants it to be a reference, but this is less useful from an ergonomic perspective).
After a quick and very unscientific look at some of the third party crates that I use, it looks like the preferred way is to simply use &str
and accepting the extra cost of allocating a new string in the function (for the cases where the caller has an owned string that it could hand over). Maybe simpler is better?