Hacker News new | ask | show | jobs
by arghwhat 3012 days ago
Ah, I found that out later but had forgotten all about it. :)

I don't remember how I found out, but it seemed oddly magical until I just now read the docs: String implements Deref<Target=str>. Makes more sense now.

I still had a bunch of to_strings()'s, though, as things tended to take String whenever I had &str's. I found this to be a very unexpected nuisance.

EDIT: Maybe I needed as_str() as the & trick doesn't work if the target type cannot be inferred as &str?

3 comments

FWIW, this is why we go over this stuff in the book now; lots of people struggle with it, it's not just you.

And yeah, Deref doesn't kick in everywhere, so you may need the .as_str() in those situations. It should be the extreme minority case, generally. Same with .to_string(), though moreso. Most stuff should take &str, not String.

It's relatively rare that APIs should be taking ownership of `String`s from you; the majority of the time arguments should be borrowed. I'm curious what cases you ran into most frequently that required `String`.
as things tended to take String whenever I had &str's

Functions should prefer &str or perhaps T where T: AsRef<str>. Note that if you write code that needs an owned String, you could consider taking some T where T: Into<String>, because this allows you to take many kinds of string types, such as &str, String, Box<str>, and Cow<'a, str>.

I don't remember the details, but I just recall that I ended up with a converting nightmare.

Your suggestions make sense, but I can't help but think that there is something fundamentally weird about basically having to use generic programming just to take a string arg. The most sensible thing would be "everything uses &str".

Your suggestions make sense, but I can't help but think that there is something fundamentally weird about basically having to use generic programming just to take a string arg.

Indeed, it's the hard trade-off to make every time need a string reference, do you want a function that is slightly more general or one that has an easy-to-read signature?

It becomes even more interesting when defining trait methods. E.g.:

    trait Hello {
      fn hello<S>(who: S) where S: AsRef<str>;
    }
Has the benefit of being more general, but cannot be used as a trait object (dynamic dispatch), since it wouldn't be possible to generate a vtable for &Hello.

I generally prefer the generic approach for functions/methods that want to own a String, since using some_str.to_owned() to pass something to a method is not very ergonomic (relative compared to &owned_string for a function that takes &str). But you have to be certain that you don't want to use the trait as a trait object.

It's just a tradeoff, like any other. If you use a generic, you add complexity, but can accept a wider set of types. If you don't, things are simpler, but you accept a smaller set of types. I personally find the AsRef to almost always be overkill. YMMV.