Converting between &str and String

Is it correct that in order to get a &str from a String in the variable s,
there is no difference between &s and s.as_str()?

Is it correct that in order to get a String from a &str in the variable s,
there is no difference between s.to_string(), String::from(s), and s.to_owned()?

Sometimes type inference fails when using &s, but should always work with s.as_str(). Apart from that they do the same thing

Yes. Actually, the implementation of s.to_string() calls String::from(s), which in turn calls s.to_owned()

5 Likes

&s has type &String while s.as_str() returns a &str. &str is the more general of the two, and a &String can be implicitly converted into a &str by Deref coercion. If the need for the coercion cannot be inferred for some reason, you could use s.as_str() instead.

When writing a function or struct, etc, you should generally use &str and not &String.

There is a similar relationship between &Vec<T> and &[T], and some other types as well.

4 Likes

I've also seen suggestions to take AsRef<str> in function arguments because it's even more general than &str.

fn foo(_: &str) {}
fn bar<S: AsRef<str>>(_: S) {}

For what sort of situations might AsRef prove superior?

There isn't much point to using AsRef<str> because the only implementations of AsRef<str> are for String and str.

An example where AsRef is useful is File::open:

pub fn open<P: AsRef<Path>>(path: P) -> Result<File>

AsRef<Path> is implemented for:

  • str
  • OsStr
  • OsString
  • Path
  • PathBuf
  • String

so File::open can take any of those as an argument.

Also Cow<'_, str>, Box<str>, Arc<str>, Rc<str>, and arbitrarily nested references, e.g. &str, &mut str, &&str, &String, &mut &Cow<'_, str>, etc. Still all of these dereference to str, so that a AsRef<str> parameter does not really make a function significantly more flexible.

2 Likes

When you need the flexibility or ergonomics of generics that it provides. @ArifRoktim demonstrated an example where AsRef<Path> is quite useful, for example; open can take a number of types which cannot necessarily be converted into each other. Another scenario that can naturally arise is when the type is buried in a container; for example f<T: AsRef<str>>(s: &[T]) { ... } could take a slice of &strs or a slice of Strings.

But if you don't have a particular reason to need it, especially for a top level parameter, I suggest sticking with &str.

1 Like

AsRef<str> is more useful when combined with other generic bounds, like Clone: It can let you duplicate a provided String, Arc<str>, &str, etc. while using the caller's preferred memory-management strategy, for example.

1 Like

I feel like in that case, it'd be better to push the clone to the call site and have the function take a String. This way, if the caller already has a String, they aren't forced to clone it. It also makes the clone more obvious.

2 Likes

Usually, that's the better course. Sometimes, though, you may need zero, one, or several clones depending on runtime considerations. Consider the (admittedly contrived) API below, for example. Depending on the surrounding program, it may make sense to use either &'a str or Rc<str> here, among others. String is suboptimal, but would also work if the duplication is worth simpler code.

#[derive(Clone)]
struct SubStr<T> { text: T, pos: Range<usize> }

impl<T:AsRef<str>> AsRef<str> for SubStr<T> { /* ... */ }
// also Deref, etc.

fn find_something_interesting<T>(text: T)->impl IntoIterator<item=SubStr<T>>
where T: AsRef<str> + Clone
{ /* ... */ }

Edit: I just realized what was bothering me about your reply:

The clone will only produce a String if the function has been passed a String in the first place. Cloning &str or Rc<str> is a pointer copy, which has the potential to avoid copying the string data; accepting a String, on the other hand, requires any internal clones to duplicate the string data.