Hi, newbie here. Just started learning rust and now trying the Rocket library.
The question is inspired with a Rocket example of paste bin implementation.
We have an ID of a paste, like this
pub struct PasteID<'a>(Cow<'a,str>);
Example shows how to make a path with it
fn upload(id: PasteID) ... {
...
let p = format!("/upload/{id}", id=id)
...Path::new(p)...
...
}
I don't actually like that and tried this instead:
error[E0277]: the trait bound `PasteID<'_>: std::convert::AsRef<std::path::Path>` is not satisfied
--> src/main.rs:8:31
|
8 | let p = Path::new("/dir").join(id);
| ^^^^ the trait `std::convert::AsRef<std::path::Path>` is not implemented for `PasteID<'_>`
What should I actually do? Really implement AsRef for PasteID?
Isn't it already implemented for Cow? If it is really needed, then how to do that correctly?
It does actually work if I cast PasteID to_string explcitely:
let p = Path::new("/dir").join(id.to_string())
but that seems weird.
So what is an idiomatic way?
If you own PasteID then why not implement AsRef<OsStr> for it? From what I can see it's just a newtype'd string, so you can defer the impl to the inner Cow<'a, str> and then you can append a PasteID to a path just fine.
Yep, I had a hope there is something like that. I tried OsStr, but the key of the solution is to use .as_ref.
Some black magic under the hood, I guess.
The working solution Rust Playground
Path doesn't own any data - it's more like str, and PathBuf is more like String in this analogy. Path::new is essentially a nicely folded away transmute that "reinterprets" a reference as itself. You can see the actual code here: path.rs - source
There's nothing implicit about it. If you look at the definition for join(), it takes something which is AsRef<Path>. Apparently a Cow<'a, OsStr> implements AsRef<Path>, but not a normal Cow<'a, str>. Hence why it's not behaving as you expect.
Most of Rust actually prefers to be explicit about something. Where "explicit" usually means there'll be something in a function signature (e.g. fn join<T: AsRef<Path>>(&self, other: T) -> PathBuf) which lets you figure out what's happening. @withoutboats has a really good article explaining the difference and Rust's philosophy.