rust学习二十.6、RUST通用范例参数默认范例和运算符重载 ...

打印 上一主题 下一主题

主题 968|帖子 968|积分 2904

一、前言

为通用范例赋予一个默认的范例,大部分的语言是没有这个特性的,但是也有例外的,例如TypeScript(可能另有别的)。
例如TypeScript可以这样使用:
  1. class MyClass<T = number> {
  2.     value: T;
  3.     constructor(value: T) {
  4.         this.value = value;
  5.     }
  6.     printValue(): void {
  7.         console.log(`Value is ${this.value}`);
  8.     }
  9. }
  10. const obj1 = new MyClass(42);  // 使用默认类型 number
  11. const obj2 = new MyClass<string>("Hello");  // 使用指定类型 string
复制代码
而运算符重载,则不少语言也支持,最典型的莫过于C++,C#.
但是rust的运算符重载是比力特殊的一种,该怎么说了?
rustc做了太多的工作,而且我觉得有点违背一些通用的设计规则。这是因为例子中的方法必须要求对象实现Copy,但是方法的参数又没有带&,会让人误会!
不喜欢有太多默认约定的设计,更喜欢每个东西都明显白白地定义。
二、通用范例参数默认范例

读取来有点拗口,意思就是:
1.在有关对象(struc,特质等)或者方法中使用通用参数T
2.可以为T指定一个默认的范例,语法是T=xxx,此中xxx是某个具体范例
如果你不喜欢T,也可以换成恣意合法的rust标识符.
就目前来看,通用参数的默认参数的作用有两点:运算符重载+方便
三、运算符重载和别的作用

3.1、运算符重载

所谓运算符重载就是除了运算符最原始的功能(编译器默认支持的)之外,还可以支持别的范例的运算数。
例如+通常用于整数、浮点数等的相加,但通过重载,别的范例对象实例也可以使用+。
以此类推,-*/等运算符号也可以。
不管怎么说,这算是一个好东西!
只不过范例参数的默认范例似乎就是为了运算符重载而存在。
3.2、别的作用

查了一些资料,据说可以联合条件编译。别的作用就是可有可无的。
条件编译示例
  1. // 定义一个特性标志,用于条件编译
  2. #[cfg(feature = "use_f64")]
  3. type DefaultNumType = f64;
  4. #[cfg(not(feature = "use_f64"))]
  5. type DefaultNumType = i32;
  6. struct Point<T = DefaultNumType> {
  7.     x: T,
  8.     y: T,
  9. }
  10. impl<T> Point<T> {
  11.     fn new(x: T, y: T) -> Self {
  12.         Point { x, y }
  13.     }
  14. }
复制代码
 
四、示例

4.1、示例代码

由于例子涉及到Add,Sub两个特质,所以先列出此二特质的定义:
  1. pub trait Add<Rhs = Self> {
  2.     type Output;
  3.     fn add(self, rhs: Rhs) -> Self::Output;
  4. }
  5. pub trait Sub<Rhs = Self> {
  6.     type Output;
  7.     fn sub(self, rhs: Rhs) -> Self::Output;
  8. }
复制代码
对书本上的例子稍微改造了下:
  1. use std::ops::{Add,Sub};
  2. #[derive(Debug, Copy, Clone, PartialEq)]
  3. struct Point {
  4.     x: i32,
  5.     y: i32,
  6. }
  7. /**
  8. * 这个使用默认类型,来自rust编程语言官方文档的例子
  9. */
  10. impl Add for Point {
  11.     type Output = Point;
  12.     fn add(self, other: Point) -> Point {
  13.         Point {
  14.             x: self.x + other.x,
  15.             y: self.y + other.y,
  16.         }
  17.     }
  18. }
  19. /**
  20. * 实现相减运算符(从而实现Point的-重载),需要实现Sub trait
  21. */
  22. impl Sub for Point {
  23.     type Output = Point;
  24.     /**
  25.      * 需要特别注意的是两个参数的定义
  26.      * self -  没有使用引用
  27.      * other - 没有要求引用
  28.      * 这种不引用的方式,不同于一般的方法定义
  29.      */
  30.     fn sub(self, other: Point) -> Point {
  31.         Point {
  32.             x: self.x - other.x,
  33.             y: self.y - other.y,
  34.         }
  35.     }
  36. }
  37. fn main() {
  38.     let p1 = Point { x: 1, y: 2 };
  39.     let p2 = Point { x: 3, y: 4 };
  40.     //使用重载的方式调用
  41.     println!("{:?}+{:?}={:?}",p1,p2, p1 + p2);
  42.     println!("{:?}-{:?}={:?}",p1,p2, p1 - p2);
  43.     //不使用重载的方式调用
  44.     let p3 = p1.add(p2).sub(p2);
  45.     let p4 = (p1.sub(p2)).add(p2);
  46.     println!("{:?}+{:?}-{:?}={:?}",p1,p2, p2,p3);
  47.     println!("{:?}-{:?}+{:?}={:?}",p1,p2,p2, p4);
  48.     let lml= Person {name: "lml".to_string()};
  49.     let ww= Animal {name: "ww".to_string()};
  50.     lml.attack(ww);
  51.     println!("{:?}",lml);
  52. }
  53. // --------------------------------------------------------
  54. // 以下的代码是为了演示 参数不带&是什么情况
  55. trait Fight {
  56.     type Item;
  57.     //fn attack(&self, other: &Self::Item);
  58.     fn attack(self, other: Self::Item);
  59. }
  60. #[derive(Debug)]
  61. struct Person {name: String}
  62. #[derive(Debug)]
  63. struct Animal {name: String}
  64. impl Fight for Person {
  65.     type Item = Animal;
  66.     fn attack(self, other: Self::Item) {
  67.         println!(
  68.             "{}攻击了{}",
  69.             self.name,
  70.             other.name
  71.         );
  72.     }   
  73. }
复制代码
这个例子做了三件事变:
1.重载+
2.重载-
3.如果不使用Copy特质会怎么样
特质Fight和结构体Person,Animal就是为了验证第3点。
4.2、名词解释

在开始执行代码前,先解释两个重要的内容
Rhs
Rhs是 "Right-Hand Side"(右侧操纵数)的缩写
关键字self和Self
仔细看看,才发现是两个,不是一个,要知道rust中是区分大小写的。
self-全小写,表示对象实例本身
Self-首字母大写,别的小写,表示范例本身
例如以下代码中:
trait Fight {    type Item;    fn attack(&self, other: &Self::Item);    fn defend(&self, danger: &T)    where T: Danger; }在方法attack中,第一个self表示具体对象实例,第二个Self则表示具体对象范例。
  1. pub trait Add<Rhs = Self> {
  2.     type Output;
  3.     fn add(self, rhs: Rhs) -> Self::Output;
  4. }
  5. pub trait Sub<Rhs = Self> {
  6.     type Output;
  7.     fn sub(self, rhs: Rhs) -> Self::Output;
  8. }
复制代码
现在代码应该容易看了。
4.3、执行

看看输出:

只要把示例中如下一部分:
  1. trait Fight {
  2.     type Item;
  3.     //fn attack(&self, other: &Self::Item);
  4.     fn attack(self, other: Self::Item);
  5. }
  6. #[derive(Debug)]
  7. struct Person {name: String}
  8. #[derive(Debug)]
  9. struct Animal {name: String}
  10. impl Fight for Person {
  11.     type Item = Animal;
  12.     fn attack(self, other: Self::Item) {
  13.         println!(
  14.             "{}攻击了{}",
  15.             self.name,
  16.             other.name
  17.         );
  18.     }    
  19. }
复制代码
 修改为:
  1. trait Fight {
  2.     type Item;
  3.     fn attack(&self, other: &Self::Item);
  4.     //fn attack(self, other: Self::Item);
  5. }
  6. #[derive(Debug)]
  7. struct Person {name: String}
  8. #[derive(Debug)]
  9. struct Animal {name: String}
  10. impl Fight for Person {
  11.     type Item = Animal;
  12.     fn attack(&self, other: &Self::Item) {
  13.         println!(
  14.             "{}攻击了{}",
  15.             self.name,
  16.             other.name
  17.         );
  18.     }    
  19. }
复制代码
再把main的调用修改为:
lml.attack(&ww);那么就可以正确输出:

为什么在Point上没有这个问题了?这是因为Point实现了Copy特质,看下面的代码:#[derive(Debug, Copy, Clone, PartialEq)]
rust的Copy特质希奇的作用:答应范例进行隐式的、按位的复制,适用于简单数据范例,避免不必要的所有权移动,提升代码效率和便利性。同时,强调其使用条件和限制,资助用户正确明白和应用
什么是按位复制?
也就是说,当赋值或作为函数参数传递时,不需要移动所有权,而是直接复制。不过,只有满足某些条件的范例才能实现Copy,比如所有字段都实现了Copy,并且范例本身没有实现Drop trait
所以,上例中,即使在方法中没有定义为引用范例,它也不会报错。而Person并没有实现Copy特质,所以会发生这个问题。
五、示例2

以下的示例演示了一个只包罗字符串切片的struct怎样相加
[code]use std:ps::Add;#[derive(Debug,Clone,Copy)]struct Name{    type Output = Name

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

宝塔山

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表