colored-rs / colored

(Rust) Coloring terminal so simple you already know how to do it !

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: How are the color methods attached to every string?

maxuuell opened this issue · comments

I used your crate in a project recently, and wanted to know how it worked.

Specifically, I wanted to understand how, by just adding

use colored::*

all of my strings suddenly got these color methods attached to them. Curious what the mechanism is that does this magic ✨

It's the colored::Colorize trait, which is implemented on &str and ColoredString. By bringing the trait into the scope of your project, you also bring into scope the implementation on foreign types (&str).

Thanks for the answer.

My understanding of traits was that you had to manually implement them if you wanted to use them. In this case, implementations on foreign types automatically "inherit" the implementations?

I don't see a lot of documentation around what foreign types are, and how this mechanism works around them.

I don't see a lot of documentation around what foreign types are

A foreign type is basically any type not defined in the crate. So, in this case, &str is a foreign type to colored as it is defined in the standard/core library. But it's not just foreign types. You also get the implementation on colored::ColoredString. For example:

use colored::ColoredString;
// use colored::Colorize;

fn main() {
    // this fails because the implementation of Colorize is not in scope
   // but the implementation of From<&'a str> for ColoredString is in scope automatically, so we can call ColoredString::from :)
    println!("{}", ColoredString::from("Hello, World!").bold());
}

My understanding of traits was that you had to manually implement them if you wanted to use them

I wouldn't say that you have to manually implement them, but they have to be implemented somewhere. For example, if you had a MyCoolStruct that you wanted to colorize, then you would need to manually impl Colorize for MyCoolStruct. Incidentally, you could not do something like impl Colorize for i32 in your own crate, because Rust does not allow implementations of foreign traits on foreign types (the orphan rule). In other words, it's only possible to impl Colorize for &'a str in the colored crate itself -- the orphan rule would actually forbid you from manually implementing Colorize for strings.

When a trait is already implemented for a type, you only have to bring the trait into scope to get its implementation. A lot of Rust's behavior works this way. Even addition is defined by a trait. Several traits are brought into scope automatically to save you time, such as the Debug trait, so you don't have to manually use std::fmt::Debug to print debug info.

It's probably easier to understand with an interactive example. You should get a pretty helpful compiler error. Can you get the example to compile? 😃
For the sake of this example, you can treat inner_scope as if it was a seperate crate.


I'm happy to continue this conversation, but I don't think this repo's issues is the right place to do it, as this conversation is starting to lean more towards the behavior of Rust's traits in general, not anything specific to colored. Feel free to open an issue in one of my repos if you have any more questions 🙂

Thank you @spenserblack for the responses. This was very thorough.

I will close this, and ask you directly if I have any more questions about the topic.

Thank you again.