TRAIT LÀ GÌ
Rust có không ít loại data types như primitives (i8, i32, str, …), struct, enum và các loại phối kết hợp (aggregate) như tuples và array. Hồ hết types không có mối tương tác nào với nhau. Những data types có những phương thức (methods) để giám sát và đo lường hay convert từ loại này sang một số loại khác, dẫu vậy chỉ khiến cho tiện lợi hơn, method chỉ là những function. Các bạn sẽ làm gì ví như một thông số là nhiều nhiều loại kiểu dữ liệu? một vài ngôn ngữ như Typescript tốt Python sẽ sở hữu được cách thực hiện Union type như thế này:
function notify(data: string | number) if (typeof data == "number") // ... else if (typeof data == "number") // ...
Còn vào Rust thì sao?

Trait là gì?Có thể các bạn đã thấy qua trait rồi: Debug, Copy, Clone, … là những trait.
Bạn đang xem: Trait là gì
Trait là 1 cơ chế abstract nhằm thêm những tính năng (functionality) tuyệt hành vi (behavior)khác nhau vào những kiểu dữ liệu (types) và tạo cho các quan hệ giữa chúng.
Trait thường đóng 2 vai trò:
Vai trò này phổ cập hơn, trait nhập vai trò là generic constraints. Dễ hiểu hơn, ví dụ, bạn định nghĩa một function, tham số là một trong kiểu tài liệu bất kỳ nào đó, không quan tâm, miễn sau kiểu dữ liệu đó phải có phương thức method_this(), method_that() nào đó đến tui. Kiểu dữ liệu nào đó điện thoại tư vấn là genetic type. Function có chứa thông số generic type này được gọi là generic function. Và bài toán ràng buộc phải gồm method_this(), method_that() , … hotline là generic constraints. Bản thân sẽ giải thích rõ cùng với các ví dụ sau dưới đây.Để đính một trait vào một type, bạn cần implement nó.Bởi vị Debug hay Copy vượt phổ biến, cần Rust có attribute để tự động implement:
#
Nhưng một số trong những trait phức hợp hơn bạn cần định nghĩa nạm thểbằng cách impl nó. Ví dụ chúng ta có trait Add(std::ops::Add)để showroom 2 type lại cùng với nhau. Nhưng Rust sẽ không biết giải pháp bạn add 2type kia lại như vậy nào, bạn phải tự định nghĩa:
use std::ops::Add;struct MyStruct number: usize,impl địa chỉ cửa hàng for MyStruct // type đầu ra = Self; fn add(self, other: Self) -> Self Self number: self.number + other.number fn main() let a1 = MyStruct number: 1 ; let a2 = MyStruct number: 2 ; let a3 = MyStruct number: 3 ; assert_eq!(a1 + a2, a3);
Note: bản thân sẽ call Define Trait là câu hỏi định nghĩa,khai báo một trait bắt đầu trong Rust (trait Add).Implement Trait là bài toán khai báo văn bản của function đượcliệu kê vào Trait cho một kiểu dữ liệu cụ thể nào kia (impl địa chỉ for MyStruct).
Định nghĩa một TraitNhắc lại là Trait định nghĩa các hành vi (behavior).Các types khác nhau có thể chia sẻ cùng cá hành vi.Định nghĩa một trait giúp nhóm những hành vi để gia công một việc gì đó.
Theo lấy ví dụ của Rust Book, lấy một ví dụ ta các struct đựng được nhiều loại text:
NewsArticle struct đựng news story, vàTweet struct có thể chứa về tối đa 280 characters cùng với metadata.Bây giờ họ cần viết 1 crate name có tên là aggregatorcó thể hiển thị summaries của data rất có thể store trên NewsArticlehoặc Tweet instance. Chúng ta cần tư tưởng method summarizetrên từng instance. Để tư tưởng một trait, ta cần sử dụng trait theo saulà trait name; cần sử dụng keyword pub nếu có mang một public trait.
pub trait Summary fn summarize(&self) -> String;
Trong ngoặc, ta định nghĩa những method signatures để có mang hành vi:fn summarize(&self) -> String. Ta hoàn toàn có thể định nghĩa văn bản của function.Hoặc không, ta sử dụng ; hoàn thành method signature, để đề nghị type nàoimplement trait Summary đều nên định nghĩa riêng mang đến nó,bởi do mỗi type (NewsArticle hay Tweet) đều phải có cách riêng để summarize. Mỗi trait tất cả thể có rất nhiều method.
Implement Trait cho một TypeBây tiếng ta định implement những method của trait Summary mang lại từng type.Ví dụ tiếp sau đây ta bao gồm struct NewsArticle với struct Tweet,và ta tư tưởng summarize mang đến 2 struct này.
pub trait Summary fn summarize(&self) -> String;pub struct NewsArticle pub headline: String, pub location: String, pub author: String, pub content: String,impl Summary for NewsArticle fn summarize(&self) -> String format!(", by ()", self.headline, self.author, self.location) pub struct Tweet pub username: String, pub content: String, pub reply: bool, pub retweet: bool,impl Summary for Tweet fn summarize(&self) -> String format!(": ", self.username, self.content)
Implement trait mang lại type giống hệt như impl bình thường,chỉ tất cả khác là ta thêm trait name cùng keyword for sau impl.Bây tiếng Summary đã làm được implement mang đến NewsArticle và Tweet,người áp dụng crate đã có thể sử dụng những phương thức của trait như các method function bình thường.Chỉ một điều khác hoàn toàn là bạn cần mang trait kia vào thuộc scope lúc này cùng với type để hoàn toàn có thể sử dụng.Ví dụ:
use aggregator::Summary, Tweet; // fn main() let tweet = Tweet username: String::from("horse_ebooks"), content: String::from("of course, as you probably already know, people"), reply: false, retweet: false, ; println!("1 new tweet: ", tweet.summarize()); // 1 new tweet: horse_ebooks: of course, as you probably already know, people
Rust Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dc563051aecebae4344776c06fb1b49d
Chúng ta có thể implement trait cho đông đảo type không giống bất kỳ, lấy một ví dụ implement Summary mang đến Vec vào scope của crate hiện nay tại.
pub trait Summary fn summarize(&self) -> String;implT> Summary for VecT> // fn summarize(&self) -> String format!("There are items in vec", self.len()) fn main() let vec = vec!<1i32, 2i32>; println!("", vec.summarize()); // There are 2 items in vec
Rust Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dcaa812fab222ec0c713a38b066bda20
Bạn sẽ không còn thể implement external traits bên trên external types.Ví dụ ta cần thiết implement Display mang đến Vec do vìDisplay cùng Vec được tư tưởng trong standard library,trong trong crate hiện tại tại. Rule này góp tránh chống chéo cánh và chắc chắn chắnrằng ko ai có thể break code của người khác với ngược lại.
Default ImplementationsĐôi khi bạn cần có default behavior mà không nhất thiết phải implement content cho từng type mỗi khi cần sử dụng:
pub trait Summary fn summarize(&self) -> String String::from("(Read more...)") pub struct NewsArticle pub headline: String, pub location: String, pub author: String, pub content: String,impl Summary for NewsArticle ; // fn main() let article = NewsArticle ... ; println!("New article: ", article.summarize()); // New article: (Read more...)
Traits as ParametersTrở lại lấy ví dụ Typescript làm việc đầu tiên, cùng với Trait bạn đã sở hữu thể definemột function đồng ý tham số là các kiểu tài liệu khác nhau.Nói theo một giải pháp khác, các bạn không cần biết kiểu dữ liệu,bạn cần biết kiểu tài liệu đó mang những behavior nào thì đúng hơn.
fn notify(data: &impl Summary) println!("News: ", data.summarize());fn main() let news = NewsArticle ; notify(news);
Ở đây, vậy vì cần biết data là type làm sao (NewsArticle tuyệt Tweet?),ta chỉ việc cho Rust compiler biết là notify sẽ gật đầu mọitype tất cả implement trait Summary, nhưng mà trait Summary có behavior .summarize(),do đó ta rất có thể sử dụng method .summary() bên trong function.
Xem thêm: Mẫu Dịch Thuật Giấy Khai Sinh Tiếng Anh Là Gì ? Giấy Khai Sinh Tiếng Anh Là Gì?
Trait BoundMột syntax sugar khác nhưng mà ta rất có thể sử dụng rứa cho &impl Summary sinh hoạt trên,gọi là trait bound, các bạn sẽ bắt gặp mặt nhiều trong Rust document:
pub fn notifyT: Summary>(item: &T) println!("News: ", item.summarize());
Đầu tiên họ định nghĩa trait bound bằng cách định nghĩamột generic type parameter trước, kế tiếp là : trong ngoặc với >.Ta có thể đọc là: item có kiểu generic là T và T nên được impl Summary.
Cú pháp này có thể dài rộng và không dễ đọc như &impl Summary, tuy nhiên hãy xem ví dụ bên dưới đây:
pub fn notify(item1: &impl Summary, item2: &impl Summary) // (1)pub fn notifyT: Summary>(item1: &T, item2: &T) // (2)
Dùng trait bound hỗ trợ chúng ta tái áp dụng lại T,mà còn giúp force item1 và item2 bao gồm cùng kiểu dữ liệu,đây là biện pháp duy tuyệt nhất (cả 2 các là NewsArticle hoặc cả hai đều là Tweet) mà lại (1) không thể.
Specifying Multiple Trait Bounds with the + SyntaxTa bao gồm cú pháp + nếu còn muốn generic T đạt được impl các trait không giống nhau.Ví dụ ta muốn item phải gồm cả Summary lẫn Display
pub fn notify(item: &(impl Summary + Display)) pub fn notifyT: Summary + Display>(item: &T)
where ClausesĐôi khi các bạn sẽ có những genenic type, từng generic type lại có khá nhiều trait bound,khiến code khó khăn đọc. Rust gồm một cú pháp where cho phép định nghĩa trait boundphía sau function signature. Ví dụ:
fn some_functionT: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
Với where clause:
fn some_functionT, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug,{
Returning Types that Implement TraitsChúng ta cũng rất có thể sử dụng impl Trait mang lại giá trị được trả về của function.
fn returns_summarizable() -> impl Summary Tweet username: String::from("horse_ebooks"), content: String::from("ahihi"), reply: false, retweet: false,
Được gọi là: function returns_summarizable() trả về bất kỳ kiểu tài liệu nào gồm impl Summary.Tuy nhiên chúng ta chỉ rất có thể return về hoặc Tweethoặc NewsArticle do cách implement của compiler. Code sau sẽ có được lỗi:
fn returns_summarizable(switch: bool) -> impl Summary if switch NewsArticle else Tweet
Rust Book tất cả một chương riêng nhằm xử lý vấn đề này: Chapter 17: Using Trait Objects That Allow for Values of Different Types
Using Trait Bounds to Conditionally Implement MethodsTa hoàn toàn có thể implement 1 method có đk cho bất kỳ type nàocó implement một trait khác cố gắng thể. Ví dụ như để dễ hiểu hơn dưới đây:
use std::fmt::Display;struct PairT> x: T, y: T,implT> PairT> fn new(x: T, y: T) -> Self Self x, y implT: Display + PartialOrd> PairT> fn cmp_display(&self) if self.x >= self.y println!("The largest thành viên is x = ", self.x); else println!("The largest member is y = ", self.y);
impl Pair implement function new trả về kiểu tài liệu Pair cùng với T là generic (bất kỳ kiểu tài liệu nào.
impl Pair implement function cmp_displaycho đều generic T với T đã có được implement Display + PartialOrdtrước đó rồi (do kia mới có thể sử dụng các behavior củaDisplay (println!("")) cùng PartialOrd (>, , …) được.
Blanket implementationsTa cũng rất có thể implement 1 trait có đk cho ngẫu nhiên kiểu dữ liệunào có implement một trait khác rồi. Implementation của một trait cho1 kiểu tài liệu khác thỏa mãn trait bound được gọi là blanket implementationsvà được sử dụng rộng thoải mái trong Rust standard library.Hơi xoắn não tuy nhiên hãy xem ví dụ dưới đây.
Ví dụ: ToString trait trongRust standard library,nó được implement cho hồ hết kiểu dữ liệu nào có được implement Display trait.
implT: Display> ToString for T // --snip--
Có nghĩa là, với đa số type có impl Display, ta bao gồm hiển nhiên thể sử dụng được những thuộc tính của trait ToString.
let s = 3.to_string(); // vị 3 thoaỏa manãn Display
Do 3 thỏa mãn nhu cầu điều kiện là đã có impl Display for i32.(https://doc.rust-lang.org/std/fmt/trait.Display.html#impl-Display-11)
Trait Inheritance
pub trait B: A
Cái này chưa hẳn gọi là Trait Inheritance, cái này đúng hơn hotline là “cái như thế nào implement cái B thì cũng bắt buộc implement chiếc A“. A với B vẫn chính là 2 trait tự do nên vẫn buộc phải implemenet cả 2.
impl B for Z impl A for Z
Inheritance thì ko được khích lệ sử dụng.
KếtCompiler áp dụng trait bound để kiểm tra những kiểu dữ liệu được sử dụng trong code tất cả đúng behavior không.Trong Python hay những ngôn ngữ dynamic typed khác, ta sẽ chạm chán lỗi dịp runtime nếu họ gọi những method màkiểu tài liệu đó không có hoặc không được định nghĩa.
Bạn có chắc chắn rằng là a sau đây có method summarize() tốt không?Nhớ rằng typing hint của Python3 chỉ có công dụng là thông báo cho lập trình viên thôi.
Xem thêm: Refer To Nghĩa Là Gì ? Nghĩa Của Từ Refer Trong Tiếng Việt Refer Tiếng Anh Là Gì
# Pythonfunc print_it(a: Union
Do đó Rust bắt được hầu như lỗi thời điểm compile time với force bọn họ phải fix hết trước lúc chương trình chạy.Do đó bọn họ không rất cần phải viết thêm code để khám nghiệm behavior (hay sự sống thọ của method)trước khi sử dụng lúc runtime nữa, tăng tốc được performance mà chưa phải từ vứt tính flexibility của generics.