【Rust 笔记】10-操作符重载

10 - 操作符重载

  • 操作符重载:让自己定义的类型支持算术和其他操作。

  • 支持操作符重载的特型:

    类别特型操作符
    一元操作符std::ops::Neg
    std::ops::Not
    -x
    !x
    算术操作符std::ops::Add
    std::ops::Sub
    std::ops::Mul
    std::ops::Div
    std::ops::Rem
    x + y
    x - y
    x * y
    x / y
    x % y
    位操作符std::ops::BitAnd
    std::ops::BitOr
    std::ops::BitXor
    std::ops::Shl
    std::ops::Shr
    x & y
    x - y
    x * y
    x / y
    x % y
    复合赋值算术操作符std::ops::AddAssign
    std::ops::SubAssign
    std::ops::MulAssign
    std::ops::DivAssign
    std::ops::RemAssign
    x += y
    x -= y
    x *= y
    x /= y
    x %= y
    复合赋值位操作符std::ops::BitAndAssign
    std::ops::BitOrAssign
    std::ops::BitXorAssing
    std::ops::ShlAssign
    std::ops::ShrAssign
    x &= y
    `x
    比较std::cmp::PartialEq
    std::cmp::PartialOrd
    x == yx != y
    x < yx <= yx > yx >= y
    索引std::ops::Index
    std::ops::IndexMut
    x[y]&x[y]
    x[y] = z&mut x[y]

10.1 - 算术与位操作符

10.1.1 - 一元操作符

  • * 引用操作符

  • std::ops::Neg 特型:实现一元取反操作符 -

    trait Neg {
      type Output;
      fn neg(self) -> Self::Output;
    }
    
  • std::ops::Not 特型:实现一元非操作符 !

    trait Not {
      type Output;
      fn not(self) -> Self::Output;
    }
    
  • 对复数值取反的泛型实现:

    use std::ops::Neg;
    
    impl<T, O> Neg for Complex<T> where T: Neg<Output=O> {
      type Output = Complex<O>;
    
      fn neg(self) -> Complex<O> {
        Complex {
          re: -self.re;
          im: -self.im
        }
      }
    }
    

10.1.2 - 二元操作符

  • Rust 的所有数值类型都实现了算术操作符;

  • Rust 的整数类型和 bool 实现了位操作符。

  • 它们也实现了接受对这些类型的引用作为一个或两个操作数的逻辑。

  • 算术操作符和位操作符的特型都有统一的形式:

    // 针对^操作符的std::ops::BitXor的定义
    trait BitXor<RHS=Self> {
      type Output;
      fn bitxor(self, rhs: RHS) -> Self::Output;
    }
    
  • 使用 + 操作符可以将一个 String 和一个 &st 切片或另一个 String 拼接起来。但是 Rust 不允许 + 的左操作数是 &str,目的是阻止通过重复小的左操作数来构建长字符串。(会造成性能隐患:所需时间与最终字符串长度的平方成正相关)

  • 要一段一段的拼接字符串,最好使用 write!

10.1.3 - 复合赋值操作符

  • Rust 的所有数值类型都实现了算术复合赋值操作符;

  • Rust 的整数类型和 bool 还实现了位复合赋值操作符。

  • Complex 类型进行 AddAssign 的泛型实现:

    use std::ops::AddAssign;
    
    impl<T> AddAssign for Complex<T> where T: AddAssign<T> {
      fn add_assign(&mut self, rhs: Complex<T>) {
        self.re += rhs.re;
        self.im += rhs.im;
      }
    }
    
  • 复合操作符的内置特型与对应的二元操作符的内置特型相互独立。

  • 实现 std::ops::Add 不会自动实现 std::ops::AddAssign。如果要让自定义类型作为 += 操作符的左操作数,那么必须实现 AddAssign

  • 与二元特型 ShlShr 类似,ShlAssignShrAssign 特型:没有将 RHS 类型参数默认为 Self,所以实现时必须明确给出右操作数的类型。

10.2 - 相等测试

  • ==!= 是对调用 std::cmp::PartialEq 特型方法 eqne 的简写:

    assert_eq!(x == y, x.eq(&y));
    assert_eq!(x != y, x.ne(&y));
    
  • std::cmp::PartialEq 的定义:

    trait PartialEq<Rhs: ?Sized = Self> {
      fn eq(&self, other: &Rhs) -> bool;
      fn ne(&self, other: &Rhs) -> bool {
        !self.eq(other)
      }
    }
    
  • Complex 的完整实现:

    impl<T: PartialEq> PartialEq for Complex<T> {
      fn eq(&self, other: &Complex<T>) -> bool {
        self.re == other.re && self.im == other.im
      }
    }
    
  • Rhs: ?Sized 放宽了 Rust 对类型参数必须有大小的限制,以支持 PartialEq<str>PartialEq<T> 这样的特型。方法 eqne 接收 &Rhs 类型的参数,可以比较 &str&[T]

  • 标准库将 Eq 定义为 PartialEq 的扩展,且没有定义新方法:

    trait Eq: PartialEq<Self> { }
    
    • Complex 类型实现 Eq:

      impl<T: Eq> Eq for Complex<T> { }
      
    • Complex 类型定义的 derive 属性中包含 Eq 也可以实现:

      #[derive(Clone, Copy, Debug, Eq, PartialEq)]
      struct Complex<T> {
        ...
      }
      

10.3 - 顺序比较

Rust 通过特型 std::cmp::PartialOrd 规定了顺序比较操作符 <><=>= 的行为:

trait PartialOrd<Rhs = Self>: PartialEq<Rhs> where Rhs: ?Sized {
  fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
  fn lt(&self, other: &Rhs) -> bool { ... }
  fn le(&self, other: &Rhs) -> bool { ... }
  fn gt(&self, other: &Rhs) -> bool { ... }
  fn ge(&self, other: &Rhs) -> bool { ... }
}

10.4-Index 与 IndexMut

  • 通过实现 std::ops::Indexstd::ops::IndexMut 特型,可以对相应类型使用类似 a[i] 这样的索引表达式。

    trait Index<Idx> {
      type Output: ?Sized;
      fn index(&self, index: Idx) -> &Self::Output;
    }
    
    trait IndexMut<Idx>: Index<Idx> {
      fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
    }
    
  • a[i..j] 是下述表达式的简写:

    *a.index(std::ops::Range { start: i, end: j })
    

10.5 - 其他操作符

*val 解引用操作符和. 调用方法的点操作符,可以使用 DerefDerefMut 特型来重载。

下述操作符都不支持重载:

  • 错误检查操作符 ? 只能用于 Result 值;
  • 逻辑操作符 &&|| 仅限于布尔值;
  • .. 操作符只能用于创建 Range 值;
  • & 操作符只能借用引用;
  • = 操作符只能转移或复制值。
  • f(x) 函数调用操作符不支持重载,如果要一个可调用的值,通常写一个闭包就可以了。

详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第十二章
原文地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

phial03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值