卖不甜枣 发表于 2024-11-23 16:57:41

rust学习十一.2、使用Trait(特质)定义通用类型的共同举动

Trait 本意是特性,特质,特性等等,其实重要指人的性格特性。不明白为什么rust的创造者不使用feature这样单词。
 
如作者所言:
Note: Traits are similar to a feature often called interfaces in other languages, although with some differences.
特性类似于别的语言的接口,但和接口还是有一些区别的。
为了便于行文,本文把Trait翻译为特质。
 
我查了一些资料,可以确认这句话基本上是对的。不对的在那里了?此处先不聊了。  总之特质在大部分情况下可以当做接口即可。
但是不能把特质称为接口,由于接口仅仅是特质的一个功能,它另有别的作用。
 
本文的内容重要都是为了通用类型服务,前面讲了许多接口特质的内容。
从本章开始,可以看到越来越多奇奇怪怪的语法,虽然我已经学过不少语言,但Rust绝对是此中的奇葩!
一、如何定义一个接口特质

所谓接口特质,即作为接口使用的特质。
就一个步骤:
pub trait Work{
    fn design(&self);
    fn code(&self);
    fn test(&self);

1.pub修饰符可选
2.一个接口特质中可以定义多个方法
这和大部分语言差不多。
二、为结构体实现一个接口特质

2.1基本实现

实现一个接口特质也很简朴,使用impl语法:
trait Summary {    fn summarize(&self) -> String;    fn get_content(&self) -> &String;    fn is_empty(&self) -> bool {        self.get_content().is_empty()    }}struct Tweet {    pub username: String,    pub content: String,    pub reply: bool,    pub retweet: bool,    pub time: String}
impl Summary for Tweet {    fn summarize(&self) -> String {        format!("{}: {}  -- {}", self.username, self.content,self.time)    }    fn get_content(&self) -> &String {        &self.content    }}
fn main(){    let tweet = Tweet {        username: String::from("勒布朗.詹姆斯"),        content: String::from("我,要退网!"),        reply: false,        retweet: false,        time: String::from("2024-11-21 05:01:01")    };    println!("{}", tweet.summarize());} 几个注意事项:
1.当实现一个接口特质的时候,必须实现这个接口特质中的所有方法,不能只有一部分
2.不能越界实现别的单位包中的接口特质。例如在单位包a存在接口特质 Ta,那么无法在单位包b中实现Ta
如果违反了,会提示:only traits defined in the current crate can be implemented for types defined outside of the crate
define and implement a trait or new type instead
3.如果接口特质T在当前单位包,那么无论对象(结构体、枚举等)O位于那里,那么都可以为O实现T
4.在同一个单位包内,你不能在差别模块为同个对象实现多次接口特质
以上第2条并不是普适的。rust的一些特质是位于标准库中,但答应你在本身的模块中实现这些接口特质,典型的是Display
2.2 默认实现

和java一样(从J8开始),rust也提供了默认实现,只不过java把接口搞得更加复杂一些。
trait Summary {
    fn summarize(&self) -> String;
    fn get_content(&self) -> &String;
    fn is_empty(&self) -> bool {
      self.get_content().is_empty()
    }
}方法is_empty就是默认的实现。这样在实今世码中,不需要提供is_empty有关的代码,也可以正常使用:
trait Summary {
    fn summarize(&self) -> String;
    fn get_content(&self) -> &String;
    fn is_empty(&self) -> bool {
      self.get_content().is_empty()
    }
}struct NewsArticle {    pub headline: String,    pub location: String,    pub author: String,    pub content: String}impl Summary for NewsArticle {    fn summarize(&self) -> String {      format!("{}, by {} ({}) \n {}", self.headline, self.author, self.location,self.content)    }    fn get_content(&self) -> &String {      &self.content    }}fn main(){    let news= NewsArticle {      headline: String::from("英雄纪念碑"),      location: String::from("中国北京"),      author: String::from("新华社记者m--泽--东"),      content: String::from("由此上溯到一千八百四十年,从那时起,为了反对内外敌人,      争取民族独立和人民自由幸福,在历次斗争中捐躯的人民英雄们永垂不朽!")    };    println!("{}", news.summarize());    println!("{}", news.is_empty());    } 
默认方法是否可以覆盖了?可以的,这个和java也一样:
impl Summary for NewsArticle {
    fn summarize(&self) -> String {
      format!("{}, by {} ({}) \n {}", self.headline, self.author, self.location,self.content)
    }
    fn get_content(&self) -> &String {
      &self.content
    }
    fn is_empty(&self) -> bool {
      println!("{}", self.content.len());
      self.content.len()<1000
    }

 
四、返回接口特质

和别的语言类似,但是这个有个题目:rust编译器只把返回的当作接口特质,而不是当作具体的对象(结构体或者枚举)。
以是,如果没有类似别的语言的强制转换,或者编译器支持,企图把返回的接口特质当作某个对象,那是不可的。
fn print(article:&impl Summary){
    println!("{}", article.get_content());

上例中,为什么my_tube.vol会陈诉非常,是由于rust编译器的逻辑只认为my_tube是一个接口特质,以是my_tube不能调用Tube的属性/方法,但是
my_tube可以调用Brush的具体方法。
 
五、资助实现通用类型函数/方法

前面的一堆内容就是为了两个目的:如何定义和实现接口特质;如何在通用类型方法中限定参数的范围。
“如何定义和实现”基本上都明白了,现在就示例下如何实现“通用类型参数限定范围”。
方式是:使用接口特质
语法: 或者,或者也可以使用where字句
/**
* 使用<T:t>的方式,是impl trait的方式的正规形式。
* 即print_normal是print的正规形式
*/
fn print_normal<T:Summary>(article:&T){
    println!("使用<T:t>的方式:{}", article.get_content());

六、小结


[*]接口特质是特质的一个部分,不能把特质翻译为接口
[*]定义接口特质还是比较简朴的。可以在一个接口特质中定义多个方法。
[*]不能在当前单位包中实现在别的单位包中定义的接口特质。但是存在例外情况,例如一些rust位于标准库中的接口特质;一个单位包内,不能在差别模块为一个接口特质,一个对象做n次实现
[*]接口特质的方法可以有默认实现;默认实现的方法可以被覆盖
[*]在方法中可以使用接口特质作为参数。有两种方式:impl,标准。前者不会强制所有参数同个类型,后者会
[*]还可以使用where字句来限定参数的接口特质。
[*]某个参数如果要绑定(限定)多个接口,可以使用+符号
[*]接口特质的出现,使得定义参数,方法变得更加灵活,也更加复杂
[*]函数/方法可以返回接口特质,但如果没有特别措施,不能把返回的效果当作具体类型使用。编译器只会把返回效果当值接口特质,即使你在方法体中明确返回的类型。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: rust学习十一.2、使用Trait(特质)定义通用类型的共同举动