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

2 Likes

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

1 Like

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.

6 Likes

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

12 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.)

4 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:.

11 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.