这是rust特质(trait)中颇有意思的一个特性。
一、媒介
这个特性,在前面已经有接触过,比方书本的第十三章节的迭代器特质就有这个例子:- impl Iterator for BooksIterator {
- type Item = String;
- fn next(&mut self) -> Option<String> {
- if self.current_index < 3 {
- let item = self.strs[self.current_index].clone();
- self.current_index += 1;
- Some(item)
- } else {
- None
- }
- }
- }
复制代码 初看这个代码,颇为迷惑,为什么要这么搞,难道用通用类型不好吗?
现在知道了,这个是rust特质的关联类型(associated type)。
很自然地有个想法,关联类型有什么用?为什么不能使用通用类型?
根据书本的示例,我自行体验了一番,总结出一点:
由于rust版本的缘故,使用关联类型更加灵活,或者说源于rust现在版本通用类型的范围性,rust的关联类型是通用类型的重要增补。
二、rust为什么要使用关联类型?
大体上可以这么说,这是rust此类语言的范围性所导致的。此类语言主张废弃继承,以为继承的坏处多于利益(当然应该是和它们的设计目标有关),比较典范的有rust和go。
然而废弃对象的继承也有很多的副作用,主要是工程上的,非性能和安全上的。
现在开发业务系统的后台语言java中,因为有了继承,它的通用类型就能够以不同于rust的方式进行使用。
举个例子:- public abstract class ExpParser<T extends ParamData> {
- }
- public class SpelParser extends ExpParser<SpelExp>{
-
- }
- public interface Study<T>{
- public void learn(T t);
- public void gather(T t);
- }
- public class Student implements Study<T>{
- public void lean(T t){
- if (t instanceof ChinaStudent){
- }
- else if (t instanceof AmericaStudent){
- }
- else{
- }
- }
- }
复制代码 这里,Java就可以方便地限定T的类型,T是ParamData的子类。此外,即使不限定T的类型,也可以简单地通过 instanceof 语法来操作。
但在rust中无法这么进行限定(通过继承指定范围),所以rust中假如要限定对象类型范围,那么就通过where语句或者关联类型来进行。
由于rust的特性,它基本上必要在编译的时候必要知道实现类或者方法的详细类型,所以某种水平上,不如具有继承特性的语言来得方便。
但rust这种关联特性也有个利益:可以在实现类/方法中指定关联类型的详细类型,从而增强了灵活性。
所以,rust通过关联类型主要办理1个问题:
可以消除通用类型的范围性。rust的通用类型在方法中必须罗列各种可能的类型,假如方法有许多实现,这就非常不方便了,但是用了关联对象就可以避免,详细是在实现对象中列出
这种方式,看起来有点像设计模式的工厂模式。
比方有以下一个比较奇怪的特质:- trait Danger{
- fn happen(&self,t:T)
- where T:Display+Clone+Go+Run+Stop+Walk+Fly;
- }
复制代码 这个是不是很古怪?
但是用了关联类型,where语句中的内容就可以分散到详细实现对象中。
下文的例子可以说明这个问题。
定义和使用关联类型
1.在特质内使用type关键字定义个关联类型(占位符)
2.在详细的实现方法中,把占位符替换为实际类型
比方:- trait Fight {
- type Item;
- fn attack(&self, other: &Self::Item);
- fn defend<T>(&self, danger: &T)
- where T: Danger;
- }
- impl Fight for Person {
- type Item = Animal;
- //其余略
- }
复制代码 在特质Fight中Item称为关联类型占位符,在详细的结构体Person中,不许把占位符替换为详细的类型(这里是Animal)。
就是这么简单!
三、示例
- trait Danger {
- fn happen(&self)->String;
- }
- struct Fire{
- address: String
- }
- impl Danger for Fire{
- fn happen(&self)->String {
- //返回address+"燃烧了"
- self.address.clone()+"燃烧了"
- }
- }
- trait Fight {
- type Item;
- fn attack(&self, other: &Self::Item);
- fn defend<T>(&self, danger: &T)
- where T: Danger;
- }
- #[derive(Debug)]
- struct Person {
- name: String,
- age: u32,
- sex: String,
- }
- #[derive(Debug)]
- struct Animal {
- name: String,
- age: u32,
- }
- impl Fight for Person {
- type Item = Animal;
- fn attack(&self, other: &Self::Item) {
- println!(
- "{}岁{}(性别:{}) 攻击了 {}岁{}",
- self.age,
- self.name,
- self.sex,
- other.age,
- other.name
- );
- }
- fn defend<T: Danger>(&self, danger: &T) {
- println!("{},{}岁{}(性别:{}) 奋起并力图战胜它们",danger.happen(), self.age, self.name, self.sex);
- }
- }
- impl Fight for Animal {
- type Item = Person;
- fn attack(&self, other: &Self::Item) {
- println!("{}岁{} 攻击了 {}岁{}", self.age, self.name, other.age, other.name);
- }
- fn defend<T: Danger>(&self, danger: &T) {
- println!("{},{}岁{} 跑了", danger.happen(),self.age, self.name);
- }
- }
- fn draw_fire() {
- println!(" ( . )");
- println!(" ) ( )");
- println!(" . ' . ' . ' .");
- println!(" ( , ) (. ) ( ', )");
- println!(" .' ) ( . ) , ( , ) ( .");
- println!(" ). , ( . ( ) ( , ') .' ( , )");
- println!(" (_,) . ), ) _) _,') (, ) '. ) ,. (' )");
- println!("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
- }
- fn draw_girl_face() {
- println!(" _____ ");
- println!(" __/ \\__ ");
- println!(" _/ (o) (o) \\_ ");
- println!(" / < \\ ");
- println!(" | \\___/ | ");
- println!(" | _________ | ");
- println!(" \\_/ \\__/ ");
- println!(" | /\\ /\\ | ");
- println!(" | | | | | ");
- println!(" | | | | | ");
- println!(" | \\/ \\/ | ");
- println!(" \\ _/ ");
- println!(" \\______/ ");
- println!(" | | ");
- println!(" | | ");
- println!(" |__| ");
- }
- fn main() {
- println!("\n火焰出现了:");
- draw_fire();
- println!("\n沐拉出现了:");
- draw_girl_face();
-
- let lu = Person { name: "沐拉".to_string(), age: 13, sex: "女".to_string() };
- let dog = Animal { name: "小狗".to_string(), age: 3 };
- let fire = Fire{address: "森林".to_string()};
- println!("{:?}", lu);
- println!("{:?}", dog);
- lu.attack(&dog);
- dog.attack(&lu);
- lu.defend(&fire);
- dog.defend(&fire);
- }
复制代码 测试输出如下:

在这个例子中,特质Fight无需在方法attack中使用where语句罗列可能的类型,而是在Person,Animal中详细说明,这样避免了attack方法看起来可笑。
通过这种方式,rust允许一个特质的方法可以和不同的类型关联起来,而又不会让特质看起来丑陋怪异,难于维护(以后要增加怎么办?)
四、小结
利用关联类型,特质可以更加方便地定义和管理类型,同时也告竣了通用的目标。
总体而言,这是一个不错的特性。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |