Why would `as_ref()` be required here rather than a reference?

After playing around with newtypes a bit, I'm curious about this use of the AsRef trait:

impl PaliSearcher {
    pub fn new(index: &PaliIndex) -> Result<Self> {
        let reader = Self::reader(index.as_ref())?;
        let uid = index.uid_field()?;
        let contents = index.contents_field()?;
        let query_parser = QueryParser::for_index(index.as_ref(), vec![contents]);

        Ok(Self {
            reader,
            query_parser,
            uid,
            contents,
        })
    }

    fn reader(index: &Index) ->  Result<IndexReader>{
        let reader = index
            .reader_builder()
            .reload_policy(ReloadPolicy::OnCommitWithDelay)
            .try_into()?;
        Ok(reader)
    }
...

I have this AsRef implementation:

impl AsRef<Index> for PaliIndex {
    fn as_ref(&self) -> &Index {
        &self.index
    }
}

But when I change the as_ref() to a normal reference like so:

let reader = Self::reader(&index)?;

I get this error:

error[E0308]: mismatched types
  --> src/search.rs:18:35
   |
18 |         let reader = Self::reader(&index)?;
   |                      ------------ ^^^^^^ expected `&Index`, found `&&PaliIndex`
   |                      |
   |                      arguments to this function are incorrect
   |
   = note: expected reference `&tantivy::Index`
              found reference `&&PaliIndex`
note: associated function defined here
  --> src/search.rs:31:8
   |
31 |     fn reader(index: &Index) ->  Result<IndexReader>{
   |        ^^^^^^ -------------
let reader = Self::reader(&index)?;

In this context, the compiler can see that reader is expecting a &Index and that you're passing a &&PaliIndex. The argument is a coercion site, but there is no coercion from &&PaliIndex to &Index, so you get an error. Note that AsRef<_> doesn't participate in such automatic coercions.

Perhaps you were thinking of Deref, which does participate in such coercions.


I think you understand this part, but anyway

let reader = Self::reader(index.as_ref())?;

In this context, the compiler can see that reader is expecting a &Index and that you're calling a method called as_ref on a &PaliIndex. So it does method call resolution for a method called as_ref and finds that <PaliIndex as AsRef<Index>>::as_ref has a receiver of type &PaliIndex and an output type of &Index; that method gets selected and everything works out.

OK I can see that. However, I can use my newtype

#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct TextUid(String);

impl AsRef<str> for TextUid {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

with Tantivy's API:

fn create_document(&mut self, text: & PaliText) -> TantivyDocument {
        let mut document = TantivyDocument::default();
        document.add_text(self.uid, &text.uid); /// Passing in a TextUid 
        for segment in text.segments.clone() {
            document.add_text(self.contents, segment.text);
        }
        document
    }

I don't know about tantivy, but I guess this api probably takes an argument of generic type, which has a AsRef<str> bound.

this is very common in rust api design.

Right you are.

No, but I was unclear as to the distinction between AsRef and Deref.

Thanks again!