rust学习二十.5、RUST特质中的关联类型

嚴華  论坛元老 | 2025-3-17 16:40:12 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1875|帖子 1875|积分 5625

这是rust特质(trait)中颇有意思的一个特性。
一、媒介

这个特性,在前面已经有接触过,比方书本的第十三章节的迭代器特质就有这个例子:
  1. impl Iterator for BooksIterator {
  2.     type Item = String;
  3.     fn next(&mut self) -> Option<String> {
  4.         if self.current_index < 3 {
  5.             let item = self.strs[self.current_index].clone();
  6.             self.current_index += 1;
  7.             Some(item)
  8.         } else {
  9.             None
  10.         }
  11.     }
  12. }
复制代码
初看这个代码,颇为迷惑,为什么要这么搞,难道用通用类型不好吗?
现在知道了,这个是rust特质的关联类型(associated type)。
 
很自然地有个想法,关联类型有什么用?为什么不能使用通用类型?
根据书本的示例,我自行体验了一番,总结出一点:
由于rust版本的缘故,使用关联类型更加灵活,或者说源于rust现在版本通用类型的范围性,rust的关联类型是通用类型的重要增补。
 
二、rust为什么要使用关联类型?

大体上可以这么说,这是rust此类语言的范围性所导致的。此类语言主张废弃继承,以为继承的坏处多于利益(当然应该是和它们的设计目标有关),比较典范的有rust和go。
然而废弃对象的继承也有很多的副作用,主要是工程上的,非性能和安全上的。
现在开发业务系统的后台语言java中,因为有了继承,它的通用类型就能够以不同于rust的方式进行使用。
举个例子:
  1. public abstract class ExpParser<T extends ParamData> {
  2. }
  3. public class SpelParser extends ExpParser<SpelExp>{
  4.    
  5. }
  6. public interface Study<T>{
  7.     public void learn(T t);
  8.     public void gather(T t);
  9. }
  10. public class Student implements Study<T>{
  11.     public void lean(T t){
  12.         if (t instanceof ChinaStudent){
  13.         }
  14.         else if (t instanceof AmericaStudent){
  15.         }
  16.         else{
  17.         }
  18.     }
  19. }
复制代码
这里,Java就可以方便地限定T的类型,T是ParamData的子类。此外,即使不限定T的类型,也可以简单地通过 instanceof 语法来操作。
但在rust中无法这么进行限定(通过继承指定范围),所以rust中假如要限定对象类型范围,那么就通过where语句或者关联类型来进行。
由于rust的特性,它基本上必要在编译的时候必要知道实现类或者方法的详细类型,所以某种水平上,不如具有继承特性的语言来得方便。
 
但rust这种关联特性也有个利益:可以在实现类/方法中指定关联类型的详细类型,从而增强了灵活性。
所以,rust通过关联类型主要办理1个问题:
可以消除通用类型的范围性。rust的通用类型在方法中必须罗列各种可能的类型,假如方法有许多实现,这就非常不方便了,但是用了关联对象就可以避免,详细是在实现对象中列出
这种方式,看起来有点像设计模式的工厂模式。
比方有以下一个比较奇怪的特质:
  1. trait Danger{
  2.     fn happen(&self,t:T)
  3.     where T:Display+Clone+Go+Run+Stop+Walk+Fly;
  4. }
复制代码
这个是不是很古怪?
但是用了关联类型,where语句中的内容就可以分散到详细实现对象中。
下文的例子可以说明这个问题。
 
定义和使用关联类型
1.在特质内使用type关键字定义个关联类型(占位符)
2.在详细的实现方法中,把占位符替换为实际类型
比方:
  1. trait Fight {
  2.     type Item;
  3.     fn attack(&self, other: &Self::Item);
  4.     fn defend<T>(&self, danger: &T)
  5.     where T: Danger;
  6. }
  7. impl Fight for Person {
  8.     type Item = Animal;
  9.     //其余略
  10. }
复制代码
在特质Fight中Item称为关联类型占位符,在详细的结构体Person中,不许把占位符替换为详细的类型(这里是Animal)。
就是这么简单!
三、示例
  1. trait Danger {
  2.     fn happen(&self)->String;
  3. }
  4. struct Fire{
  5.     address: String
  6. }
  7. impl Danger for Fire{
  8.     fn happen(&self)->String {
  9.         //返回address+"燃烧了"
  10.         self.address.clone()+"燃烧了"
  11.     }
  12. }
  13. trait Fight {
  14.     type Item;
  15.     fn attack(&self, other: &Self::Item);
  16.     fn defend<T>(&self, danger: &T)
  17.     where T: Danger;
  18. }
  19. #[derive(Debug)]
  20. struct Person {
  21.     name: String,
  22.     age: u32,
  23.     sex: String,
  24. }
  25. #[derive(Debug)]
  26. struct Animal {
  27.     name: String,
  28.     age: u32,
  29. }
  30. impl Fight for Person {
  31.     type Item = Animal;
  32.     fn attack(&self, other: &Self::Item) {
  33.         println!(
  34.             "{}岁{}(性别:{}) 攻击了 {}岁{}",
  35.             self.age,
  36.             self.name,
  37.             self.sex,
  38.             other.age,
  39.             other.name
  40.         );
  41.     }
  42.     fn defend<T: Danger>(&self, danger: &T) {
  43.         println!("{},{}岁{}(性别:{}) 奋起并力图战胜它们",danger.happen(), self.age, self.name, self.sex);
  44.     }
  45. }
  46. impl Fight for Animal {
  47.     type Item = Person;
  48.     fn attack(&self, other: &Self::Item) {
  49.         println!("{}岁{} 攻击了 {}岁{}", self.age, self.name, other.age, other.name);
  50.     }
  51.     fn defend<T: Danger>(&self, danger: &T) {
  52.         println!("{},{}岁{} 跑了", danger.happen(),self.age, self.name);
  53.     }
  54. }
  55. fn draw_fire() {
  56.     println!("       (  .      )");
  57.     println!("     )           (              )");
  58.     println!("             .  '   .   '  .  '  .");
  59.     println!("    (    , )       (.   )  (   ',    )");
  60.     println!("     .' ) ( . )    ,  ( ,     )   ( .");
  61.     println!("  ). , ( .   (  ) ( , ')  .' (  ,    )");
  62.     println!("  (_,) . ), ) _) _,')  (, ) '. )  ,. (' )");
  63.     println!("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
  64. }
  65. fn draw_girl_face() {
  66.     println!("         _____         ");
  67.     println!("      __/     \\__      ");
  68.     println!("    _/  (o) (o)  \\_    ");
  69.     println!("   /      <       \\   ");
  70.     println!("  |     \\___/      |  ");
  71.     println!("  |   _________    |  ");
  72.     println!("   \\_/         \\__/   ");
  73.     println!("    |  /\\ /\\    |     ");
  74.     println!("    | |  |  |   |     ");
  75.     println!("    | |  |  |   |     ");
  76.     println!("    |  \\/ \\/    |     ");
  77.     println!("     \\        _/      ");
  78.     println!("      \\______/        ");
  79.     println!("        |  |          ");
  80.     println!("        |  |          ");
  81.     println!("        |__|          ");
  82. }
  83. fn main() {
  84.     println!("\n火焰出现了:");
  85.     draw_fire();
  86.     println!("\n沐拉出现了:");
  87.     draw_girl_face();
  88.    
  89.     let lu = Person { name: "沐拉".to_string(), age: 13, sex: "女".to_string() };
  90.     let dog = Animal { name: "小狗".to_string(), age: 3 };
  91.     let fire = Fire{address: "森林".to_string()};
  92.     println!("{:?}", lu);
  93.     println!("{:?}", dog);
  94.     lu.attack(&dog);
  95.     dog.attack(&lu);
  96.     lu.defend(&fire);
  97.     dog.defend(&fire);
  98. }
复制代码
测试输出如下:

在这个例子中,特质Fight无需在方法attack中使用where语句罗列可能的类型,而是在Person,Animal中详细说明,这样避免了attack方法看起来可笑。
通过这种方式,rust允许一个特质的方法可以和不同的类型关联起来,而又不会让特质看起来丑陋怪异,难于维护(以后要增加怎么办?)
四、小结

利用关联类型,特质可以更加方便地定义和管理类型,同时也告竣了通用的目标。
总体而言,这是一个不错的特性。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

嚴華

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表