pub fn read_directory<T>(dir: T)
where
T: AsRef<Path>,
{
later on in body of this fn:
if dir.parent()//but here I'm getting error:
^^^^^^ method not found in `T
How so???
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?
The 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 Deref and DerefMut, but you should generally avoid using them as trait bounds.
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 &self
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.
Because AsRef is not Deref. 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. Deref and 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 AsRef
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 T implements 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.
Note that 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 implementation.
Since 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).