Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Step-by-step, how to understand the describe() function example

(assuming I understand this, which is an if, a small if, but an if)

Part A

...

We can implement methods directly on shared reference types themselves. There is no deref coercion then when called on a shared reference that is of that shared reference type (playground)

In a sense then the method implemented, when called on a shared reference, takes ownership of the shared reference, per se, not the value the shared reference points to.

Code Block
languagerust
use std::borrow::Cow;

trait CustomInto<T> {
    fn custom_into(self) -> T;
}

impl<'a> CustomInto<Cow<'a, str>> for &'a str {
    fn custom_into(self) -> Cow<'a, str> {
        Cow::Borrowed(self)
    }
}

fn main(){
    let _cow = "hello there".custom_into();    
}

In this code, Note how the implementation is for the ref string slice type, not the primitive string slice type.

Part B

...

  1. Consider two traits

    1. From<String>

    2. From<&'a str>

  2. Cow<'a, str> The enum Cow<str> implements both traits

    1. Cow has the associated function
      fn from(s: String) → Cow<'a, str>Cow<str>

    2. Cow has another associated function
      fn from(s: &'a str) → Cow<'a, str>

  3. Therefore String implements<

  4. Consider the additional trait

    1. Into<Cow<'a, str>>

  5. String automatically implements Into, as does str, given that the From traits are implemented

  6. Both String and str implement Into with a method of the following form

    1. fn into(self) → Cow<a', str>Cow<str>

  7. Consider another trait

    1. Into<Cow<str>>

  8. A From implementation implies that there is a corresponding Into implementation. There is a blanket implementation of Into in the standard library. My guess is that the following code is the blanket implementation

    Code Block
    languagerust
    impl<T,U> Into<U> for T
       where U: From<T> {
       
      fn into(self) -> U {
        U::from(self)
      }  
    }
  9. Therefore String implements the Into<Cow<str>> trait

    Code Block
    languagerust
    impl<'a> Into<Cow<'a, str>> for String {
      fn into(self) -> Cow<'a, str> {
        ...
      }
    }
  10. Also &str implements the Into<Cow<str>> trait

    Code Block
    impl<'a> Into<Cow<'a, str>> for &'a str {
      fn into(self) -> Cow<'a, str> {
        ...
      }
    }
  11. In the first four arms of the match expression, there are string literals upon which the method into() is called. We know the methods must have a return type of these method calls must return a Cow<'static, str>.

  12. A string literal is of type &str, a shared reference to a string slice

  13. A string literal has a static lifetime.

  14. When applying the method Therefore the result of calling the into() method on a string literal is a Cow<'static, the string literal is dereferenced into a string slice strThen the method into() can be applied on the str. We have already seen that this method existsstr>

  15. So, we see now how the first four arms of the match expression work.

  16. In the last arm of the match expression, there is a String upon which the method into() is called. This method call must return a Cow<'static, str>

  17. We can call the into() method on a String to get a Cow<str> without any lifetime limitations between the String and the Cow<str>

  18. So the returned Cow<str> can be annotated with a static lifetime, without requiring that the String had to have one, too.

  19. So, we see now how the fifth arm of the match expression works.