pub fn read_directory<T>(dir: T)
later on in body of this fn:
if dir.parent()//but here I'm getting error:
^^^^^^ method not found in `T
My understanding of the construct is:
dir is a of type T that implements AsRef < Path > . Path has the method "parent", so why the compiler cannot see that the method exists?
because it is not
Path but rather
AsRef<T> so you should directly get reference first via
You need to call
as_ref() on it to actually get a
&Path you can call the method on.
AsRef trait is nothing special. It just requires
T to implement a method
fn as_ref(&self) -> &Path, which you have to call if you want to use
Path's methods. The only traits that do this operation implicitly are
DerefMut, but you should generally avoid using them as trait bounds.
OK, thanks guys. Clarified, but still, weird... Why there is no auto deref here is beyond me.
Generally Rust prefer explicitness, except few special cases of type coercion Type coercions - The Rust Reference (rust-lang.org)
Yes, but what non-obvious thing could happen here? I can understand being explicit about having to write clone() and to_string(), but here? What are we being explicit about? The dir object is already AsRef so having to say yet again, as_ref on AsRef is not being explicit, it being repetitive without deeper sense.
AsRef is trait, which implementation is never going to be noop without optimizations.
By forcing you to call
as_ref() directly it hints you that it is not noop operation, in fact depending on operation it might be something more complicated than just
If it was implicit then each time you use
dir as a
Path, even just converting it to a
&Path, will call a method, which can do anything.
What method will it call?
It will have to call
dir.as_ref(), otherwise how can you get an
My point exactly. Why do I have to spell it out? Why cannot be done by compiler?
To expand, having type implementing AsRef interface, it should be obvious to the compiler that in order to access that object as_ref needs to be called. Same with auto deref on smart pointers etc. It is simply inconsistent.
AsRef is not
Deref is special, since it is associated with the operator - when
x is not a reference or pointer,
*x is desugared to
*(x.deref()). But for
AsRef there's no such operation - it's just a trait like any you can create manually.
I understand it, but it simply doesn't make sense, or rather, it makes one explicitly state what compiler could do and there is no other way to do it as to call as_ref. Pointless repetition in my opinion. Too verbal without much point. Don't get me wrong, I do like the fact that rust prefers being explicit, but there are situations where this is just not being explicit but pointlessly repetitive.
It could be done by the compiler, but the reason is that it would be bad if that was the case. Having multiple hidden function calls is bad, so they should be minimized as much as possible.
DerefMut are an exception because sometimes you really really want this behaviour, but part of the reason this is allowed is because this is limited to them.
In your case the best solution would be doing
let dir = dir.as_ref() at the start of your function. You do only one function call, avoiding potential inconsistent results from the multiple
.as_ref(), while also not having to repeat yourself. Note though that this isn't a valid solution for the compiler: in the general case you may use an
&mut T, which must invalidate the result of the
.as_ref() call, so you would end up with even more unpredictable hidden function calls.
If it doesn't make sense to you, it doesn't mean there is no sense. Just get over with it and accept it as fact. If you do not want to call
AsRef::as_ref then just accept
&Path as parameter rather than generic with
Oh, and in addition to my previous comment: I think there could be a way the compiler does autoderef with
AsRef, but I'm not entirely sure.
Deref has an associated type which makes it clear what type it should autoderef into, but
AsRef has a generic parameter, so a type may implement multiple
AsRefs, which could potentially made the target type ambiguous. For example any type
AsRef<T>, so should that be considered? It could create infinitely many autoderef targets. Should
str have all the
[u8] methods directly available? It implements
AsRef<[u8]> after all.
In addition to this, as noted previously
AsRef<T> could do literally anything. Frankly, I prefer the explicitness of
as_ref() over auto-as_ref()-ish behavior from the compiler. It would be extremely frustrating and annoying if the compiler did this automatically but someone implemented
AsRef<T> to do something over HTTP, or do something else that is really slow, and then me being unable to figure out what's causing it because the problem isn't in my code and its not obvious (even in a debugger) what I'm doing to make that behavior occur.
most things (as SkiFire13 notes below, only Path and PathBuf) which are
T: AsRef<Path> would, in practice, implement
T: Deref<Output=Path>, so you're not meaningfully restricting yourself by accepting
&Path and passing a reference at the call site. Most of the time it's better to be non-generic, for both improved code clarity and performance.
AsRef is more valuable for other types, where there may be no
Deref is called automatically by deref coercions, it is strongly discouraged to implement
Deref for anything other than a smart pointer. For example, if you make a wrapper
struct Foo(Bar), then it's not a good practice to
impl Deref for Foo: that's both unidiomatic and can lead to unexpected conversions. You are, however, welcome to
impl AsRef<Bar> for Foo. This has an additional benefit that you can unambiguously
impl AsRef<R> for T for several different
R's and the same
T (whether it is actually a good idea is up to you).