McGrady
McGrady

Reputation: 11477

Does the Rust compiler convert String to &str implicitly?

I implemented the trait Foo for &'a str, but when I call the method test for std::string::String, it works. I checked my code and I'm sure that I don't implement the trait for String, so does the Rust compiler convert String to str implicitly?

The code likes this:

pub trait Foo {
    fn test(self) -> String;
}

impl<'a> Foo for &'a str {
    fn test(self) -> String {
        String::from("da")
    }
}

fn main() {
    let s = String::from("123456789");
    println!("{}", s.test());
}

The Rust plugin for the Jetbrains IDE can't find test method for String, but it works well.

Why does that code work?

Upvotes: 0

Views: 214

Answers (1)

S&#233;bastien Renauld
S&#233;bastien Renauld

Reputation: 19662

First off, careful. String != CString. And s.test(var) will not yield anything but an error since your test method takes no parameter but self.

Now, on to what is actually happening.

When you define an extension method like you did by defining a trait then implementing it for &_ str but not on String, and then try to invoke it, the compiler implicitly does the dereferencing (since String implements Deref into &_ str).

As a result, your main function can be written for clarity as:

fn main() {
    let s = String::from("123456789");
    println!("{}", (s.deref()).test());
}

and is entirely analogous to what the compiler does under-the-hood. The compiler essentially saw that you were trying to use this, and did so for you.

I need to warn you, though - your code is extremely weird in multiple ways:

  • One normally implements trait methods like these on objects, not their Deref counterpart, precisely to avoid this level of indirection
  • Your implementation consumes a reference. Consider this for a moment.

A much better, and less indirect way to write it, is as follows:

pub trait Foo {
    fn test(&self) -> String;
}

impl Foo for str {
    fn test(&self) -> String {
        String::from("da")
    }
}

fn main() {
    let s = String::from("123456789");
    println!("{}", s.test());
}

You are not sacrificing anything, the functionality remains identical, but:

  • You are no longer needlessly consuming a reference
  • You are no longer needlessly forcing the compiler to infer a deref call

Upvotes: 3

Related Questions