Because to_string allocates. And if a function requires a String (owned), it cannot accept a str reference (borrowed), it would defeat the purpose of the strong type system. String is moved, while str is passed by reference.
There is the exception of Deref. If the function requires type A, and you pass it type B, which Derefs into type A, the compiler will Deref it for you. But that is zero cost and panic free, whereas allocating (and copying) an owned type from a reference isn't. In Rust you have to be explicit.
Anyway, using String in function signatures is most often not the best choice. If you will internally be required to use a String, it's better to ask for a type "impl Into<String>", you'd call into() inside your function. And in the most common case, where you require a &str or can convert to your type from it, the best choice is an "impl AsRef<str>" and you can call as_ref() on it to get a str reference, which you can wrap in a Box, Rc, Arc, String, or your custom type, or pass it directly to other functions. All of those, Box<str>, Rc<str>, etc implement both traits.
Using impl Trait, you avoid having to declare generic parameters.
If only one could construct a macro to solve the boilerplate of AsRef<str> etc ;)
To me, there’s not much difference between:
fn foo(name: &str) {}
and*:
fn foo(name: impl AsRef<str>) {}
They’re different kinds of strings, the String string means heap allocation, and Rust never allocates in the language, so the compiler automatically invoking allocation routines for you wouldn’t be good, in Rust’s view.
Those "things that look at a lot like strings already" are literals and thus constants, with the type `&'static str` which is how Rust spells an immutable reference to a string slice which lives forever. So, Rust is promising that the string slices "Hate Me" and "Blue October" exist, and in practice probably if you look inside the resulting executable it says "Hate MeBlue October" or similar.
On the other hand the String type is a growable array of bytes used to assemble strings, it's an owning type, so Song will own those two Strings, they can't go away or be altered via some other force. Owning a string would allow you to add to it, change it, or discard it entirely, you obviously can't do that to the text baked into the executable, so if you want to own a String it will have to be allocated at runtime.
You can also write "Some Words".to_owned() for example to the same effect.
To be fair, as much as Rust macros are nice, it is a losing battle to go up against the Lisp macro system. Lisp might have its flaws, but macros in Lisp are second to none.
When to use them is a whole different story. But examples of macros I like are `when` and `unless`. Yes, simple, but they show a nice example of their power.
For more complicated once, love it or hate it, but the `loop` macro is probably THE prime example of a powerful macro.
When you have a &str (like "Blue October") and pass it to something that wants a String, you can do .into() instead of .to_string()
It’s shorter to write and takes up a little less space on screen to. I almost always use .into() when like in your example I initialize String field members of a struct in Rust.
That's the price you pay for a low level language where things like memory usage are visible for you to optimize.
If you don't see the gain, maybe Rust is not the right language for your use-case.
the reasons to not use macros have to do with hygeinicity? -- macros can do things like introduce code that is hard to understand, perhaps a dynamically named function, or a difficult to chase dependency or import... moreover these can make grepping harder.