What I understand from the above code snippet is that we want to convert &String to &str.
why does the as_ref method return self in the above code snippet? Because the self is String, we want to convert &String to &str, so the as_ref should not return self, it should somehow convert String into str and then return a reference to str or it simply returns &self that will convert it into &str.
At least the return expression of the as_ref method should be self.as_str(). Also, I found that the as_ref method of String also returns a plain self.
I think I am clearly missing something to connect the dots.
Deref coercion converts a reference to a type that implements the Deref trait into a reference to another type. For example, deref coercion can convert &String to &str because String implements the Deref trait such that it returns &str .
I thought that inside the body of the as_ref method, the self is simply a plain self. From your comment now I understand that inside the method body, the self remains behind reference.
I meant that deref coercions also implicitly convert a reference type to another, so it could have served the role the AsRef trait serves
No, it is declared as &self in the method signature, which is what matters. &self as a receiver means self: &Self, ie. the variable self has type "reference to the type being implemented".
I meant that deref coercions also implicitly convert a reference type to another, so it could have served the role the AsRef trait serves
No, they have intentionally different roles. Deref is for smart pointers when there is a single unambiguous type to which the smart pointer dereferences. AsRef, in contrast, is for generic reference-to-reference conversions. For example, String implements both AsRef<str> and AsRef<[u8]>.
There's a really important difference between Deref and AsRef; for a given type T (e.g. String), you can only have one implementation of Deref, which tells you what type you get during deref coercion, but you can have as many implementations of AsRef as you find useful.
So &String becomes &str via deref coercion - nothing else is possible - while it has implementations of AsRef<str>, AsRef<[u8]>, AsRef<OsStr> and AsRef<Path> to let you convert an &String into an &[u8], &OsStr, &Path etc.
Others have given more detail, but to make sure the core point is clear: it's for writing generic functions, like fn foo<S: AsRef<str>>(input: &S) or fn foo<B: AsRef<[u8]>>(input: &B)