Hi, everyone, this is my fourth blog post for rust, in this blog post, I will talk about something about trait in rust. This blog might be more likely to a summary of my views of traits and interface.
The first time I come into rust and get to know that there is a concept named trait which is much similar to interface in other languages such as java. Then I just think ”haha, that should be easy to learn”.
Interface I usually use in java:
Basic use:
interface Animal { public void eat(); public void travel(); } public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
other use in spring framework to isolate the implementation and the raw interface.
public interface CommentService { void addNewComment(Comment comment); void deleteCommentByid(int commentId); void praiseCommentById(Integer commentId); List<Comment> getAllComment(); }
So, the first time I see implementing a trait for struct in rust: Ok, I get it, quite similar to java.
Then, the first time I see implementing a trait for enum in rust: I said: ”???”
Code here.
pub enum Die { D4, D6, D8, D10, D12, D20, } trait Rollable { fn roll(&self) -> u8; } impl Rollable for Coin { fn roll(&self) -> u8 { get_random_value(2) } } impl Rollable for Die { fn roll(&self) -> u8 { match *self { Die::D4 => get_random_value(4), Die::D6 => get_random_value(6), Die::D8 => get_random_value(8), Die::D10 => get_random_value(10), Die::D12 => get_random_value(12), Die::D20 => get_random_value(20), } }
Actually, I was so confused: Wow, why can I do it? This is a enum! But why it can be implemented with traits?
To clearly solve this question, we need to clarify:
Firstly, we can implement the struct with our methods using impl , right? Trait just provide a reusable method set with no implementation, this is much more similar to the interface in java and easy to understand.
Then, let’s check out the enum in rust. Enum in rust is much more powerful than that in java. It can carry some data! I think this is the primary reason that why we are allowed to implement trait into the enum. Even If the enum in rust is just simple enough and do not have followed data, do not forget we usually use match to resolve the enum, then, we can place some code in the match block of each case. If these code in the match block can be similar, things could be simplified: we can use trait to replace them! Then, we just need to implement trait for the enum and use the method in trait for every case (or item) in the enum!
If there are some data followed up in the item of enum, then this can be much more better: just put your data into the implemented trait method, then everything finished.
I just feel that rust is trying to make enum as powerful as it can, every item in the enum is just like separated struct (might be inaccurate but still feel like this), then they can make full use of trait. Therefore, the use of trait has been wider and help to form a much more special format for “interface” in rust. This is quite cool!
Then, let’s come to the basic data type, we will talk about i32 here.
• I32 has implemented these traits: Copy • Clone • Add, Sub, Mul, Div, Rem • AddAssign, SubAssign, MulAssign, DivAssign, RemAssign • PartialEq, Eq • PartialOrd, Ord • Debug • Hash • Sum,Product • From<T>,Into<T> • Default
You can see, a large part of method for i32 are implemented with trait, this just reversed what I thought I knew. That is because I use java most of the time before, and, for Integer in java, some of its method are concluded by the class itself, and a small portion of the methods are implemented with interfaces. “Rust's design philosophy encourages the use of traits to define shared behavior.” This is what I get from one blog. Now I have to say, it’s really amazing.
Even in the println!, like println!("{}",value);
the value needs to implement certain traits, then it can be formated and printed out.
That is to say,
For a type's value to be processed by the println! macro, the type must implement at least one of the std::fmd::Display or std::fmt::Debug traits.
Additionally, there are other specialized formatting traits, such as std::fmt::Octal, std::fmt::Pointer, std::fmt::Binary, etc., that allow using specific formatting options within the println! macro.
pretty amazing design!