Right, the reason why folks (rightly) recommended against to_string() in the past is because it went through the formatting machinery. Back then, we didn't have specialization at all, so all we had was the blanket impl on ToString for all T: std::fmt::Display. So even for &str, it would need to go through the formatting machinery.
But not too long after Rust 1.0, we got specialization and this was one of the first things we used it: to "fix" the ToString impl for str so that it did the obvious and cheapest thing.
Sadly, specialization never got fully stabilized, but it's used in a smattering of places inside of std to speed things like this up.
Therefore, today, to_string() and to_owned() and String::from are all equivalent from a perf perspective. I mostly see folks using to_string() (including myself), but I don't think any choice is particularly wrong.
Personally, I distinguish between to_owned and clone more than I think most people do. I like using to_owned for all &T -> T things, even where clone would work, and instead keep clone for what I think of as T -> (T, T) things (even though Clone in implementation is also &T -> T).
As such, there are times when I'd use all of the options, depending what I'm doing:
If I already have a String, but need another one for a bit because I have to change it to give to something else, I'd .clone() it.
If I have a &str (or &String if that happened from an iterator adapter or something) that I need to edit, I'll use .to_owned() on it (same as I would if I had a &[u8] and wanted a Vec).
If something takes a name: String then I'll use .to_string() because the fact that it's a &str I'm starting with isn't important -- if it was a Uuid or u64 or something I'd do the same.
If I'm calling something else and don't really care, I'd likely just use .into(), like foo(s.into()), same as I'd do if I happen to have a u32 and it wants a u64 or something like that.
But I wouldn't say that's the modern convention, just my taste.