What is the difference between as_ref() & '&'?

I know that as_ref() is a trait which is used for reference-to-reference conversion.I think with the '&' also we can do the same . in that case

  1. what exactly is the usecase for as_ref() ?

  2. when to use '&' and when to 'as_ref()' ?

  3. I was trying below code and it gave error at line no 3. But line no 4 which has explicit type defined as &str works fine. why is it so? .

  let a = String::from("foo");
  let b = &a;
  let c = a.as_ref(); //why this line gives compilation error
  let d:&str = a.as_ref(); //and why this line works

& does that via deref coercions. That works only for things that implement the Deref trait.

To answer question 3, the trait AsRef is implemented for String for multiple output types, as indicated by the error message:

  = note: multiple `impl`s satisfying `String: AsRef<_>` found in the following crates: `alloc`, `std`:
          - impl AsRef<OsStr> for String;
          - impl AsRef<Path> for String;
          - impl AsRef<[u8]> for String;
          - impl AsRef<str> for String;

Rust cannot infer which type is required, so the compiler requests an explicit type.

2 Likes

It's different for Option, where & gives you &Option<T>, and .as_ref() gives you a much more useful Option<&T>.

5 Likes

Generic code where you've intentionally added an AsRef bound only. You should never use AsRef::as_ref outside generic code, as it tends to make your code break later. If you want a reference to a, use &a.

(For clarity: this statement is only about the trait method as_ref. Inherent methods like Option::as_ref are fine in non-generic code.)

2 Likes

I think it's important to point out that as_ref really can be different methods.

If I understand it right, then the first isn't the same as the last two, but has the same name.

The first as_ref is doing a cheap conversion from one type to another (more precisely it converts a reference to a value into a reference to a value of a different type), while the last two create an owned Options/Results from a reference to an Option or a Result.


Compare:

pub trait AsRef<T: ?Sized> {
    fn as_ref(&self) -> &T;
}

With:

impl<T> Option<T> {
   pub const fn as_ref(&self) -> Option<&T> {
        match *self {
            Some(ref x) => Some(x),
            None => None,
        }
    }
}

The first one returns a reference (&T), the second one returns an owned value (Option<_>).


So we're talking about three things:

  • &x turns a value into a reference to that value.
  • Calling .as_ref() on a value (or reference to a value) of a type U that implements the AsRef<T> trait will perform a cheap conversion from &U to &T.
  • Calling .as_ref() on an &Option<T> or &Result<T, E> will convert them into an owned Option<&T> or Result<&T, &E>.

Pretty confusing :grin:.

2 Likes