1 - ops模块概述

可重载的算符

ops = Overloadable operators = 可重载的运算符

官方文档

https://doc.rust-lang.org/core/ops/

可重载的运算符。

实现这些 traits 可使您重载某些运算符。

其中的某些 traits 由 prelude 导入,因此在每个 Rust 程序中都可用。只能重载由 traits 支持的运算符。 例如,可以通过 Add trait 重载加法运算符 (+),但是由于赋值运算符 (=) 没有后备 trait,因此无法重载其语义。 此外,此模块不提供任何机制来创建新的运算符。 如果需要无特征重载或自定义运算符,则应使用宏或编译器插件来扩展 Rust 的语法。

考虑到它们的通常含义和 运算符优先级,运算符 traits 的实现在它们各自的上下文中应该不足为奇。 例如,当实现 Mul 时,该操作应与乘法有些相似 (并共享期望的属性,如关联性)。

请注意,&&|| 运算符发生短路,即,它们仅在第二操作数对结果有贡献的情况下才对其求值。由于 traits 无法强制执行此行为,因此不支持 &&|| 作为可重载的运算符。

许多运算符都按值取其操作数。在涉及内置类型的非泛型上下文中,这通常不是问题。 但是,如果必须重用值而不是让运算符使用它们,那么在泛型代码中使用这些运算符就需要引起注意。一种选择是偶尔使用 clone。 另一个选择是依靠所涉及的类型,为引用提供其他运算符实现。 例如,对于应该支持加法的用户定义类型 T,将 T&T 都实现 traits AddAdd<&T> 可能是一个好主意,这样就可以编写泛型代码而不必进行不必要的克隆。

示例

本示例创建一个实现 AddSubPoint 结构体,然后演示加减两个 Point。

use std::ops::{Add, Sub};

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {x: self.x + other.x, y: self.y + other.y}
    }
}

impl Sub for Point {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Self {x: self.x - other.x, y: self.y - other.y}
    }
}

有关示例实现,请参见每个 trait 的文档。

FnFnMutFnOnce traits 由可以像函数一样调用的类型实现。请注意,Fn 占用 &selfFnMut 占用 &mut selfFnOnce 占用 self。 这些对应于可以在实例上调用的三种方法:引用调用、可变引用调用和值调用。 这些 traits 的最常见用法是充当以函数或闭包为参数的高级函数的界限。

Fn 作为参数:

fn call_with_one<F>(func: F) -> usize
    where F: Fn(usize) -> usize
{
    func(1)
}

let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);

FnMut 作为参数:

fn do_twice<F>(mut func: F)
    where F: FnMut()
{
    func();
    func();
}

let mut x: usize = 1;
{
    let add_two_to_x = || x += 2;
    do_twice(add_two_to_x);
}

assert_eq!(x, 5);

FnOnce 作为参数:

fn consume_with_relish<F>(func: F)
    where F: FnOnce() -> String
{
    // `func` 使用其捕获的变量,因此不能多次运行
    //
    println!("Consumed: {}", func());

    println!("Delicious!");

    // 再次尝试调用 `func()` 将为 `func` 引发 `use of moved value` 错误
    //
}

let x = String::from("x");
let consume_and_return_x = move || x;
consume_with_relish(consume_and_return_x);

// `consume_and_return_x` 现在不能再被调用

2 - ops function

Fn,FnMut和FnOnce

2.1 - trait core::ops::FnOnce

Fn 接受类似函数类型的参数并且只需要调用一次

源码

Trait core::ops::FnOnce 的定义如下:

pub trait FnOnce<Args> {
    /// The returned type after the call operator is used.
    #[lang = "fn_once_output"]
    #[stable(feature = "fn_once_output", since = "1.12.0")]
    type Output;

    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

官方文档

https://doc.rust-lang.org/core/ops/trait.FnOnce.html

具有按值接收者的调用运算符的版本。

可以调用 FnOnce 的实例,但可能无法多次调用。因此,如果唯一知道类型的是它实现 FnOnce,则只能调用一次。

FnOnce 由可能消耗捕获变量的闭包以及实现 FnMut 的所有类型 (例如 (safe) 函数指针 (因为 FnOnceFnMut 的特征) ) 自动实现。

由于 FnFnMut 都是 FnOnce 的子特性,因此可以在期望使用 FnOnce 的情况下使用 FnFnMut 的任何实例。

当您想接受类似函数类型的参数并且只需要调用一次时,可以使用 FnOnce 作为绑定。 如果需要重复调用该参数,请使用 FnMut 作为界限; 如果还需要它不改变状态,请使用 Fn

有关此主题的更多信息,请参见 Rust 编程语言 中关于闭包的章节。

还要注意的是 Fn traits 的特殊语法 (例如 Fn(usize, bool) -> usize)。对此技术细节感兴趣的人可以参考 Rustonomicon 中的相关部分

示例

使用 FnOnce 参数:

fn consume_with_relish<F>(func: F)
    where F: FnOnce() -> String
{
    // `func` 使用其捕获的变量,因此不能多次运行。
    println!("Consumed: {}", func());

    println!("Delicious!");

    // 再次尝试调用 `func()` 将为 `func` 引发 `use of moved value` 错误。
}

let x = String::from("x");
let consume_and_return_x = move || x;
consume_with_relish(consume_and_return_x);

// `consume_and_return_x` 现在不能再被调用

2.2 - trait core::ops::FnMut

想接受类似函数类型的参数并需要反复调用它,同时允许其改变状态时使用FnMut

源码

Trait core::ops::FnMut 的定义如下:

pub trait FnMut<Args>: FnOnce<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

官方文档

https://doc.rust-lang.org/core/ops/trait.Fn.html

采用可变接收者的调用运算符的版本。

FnMut 的实例可以重复调用,并且可以改变状态。

FnMut 由闭包自动实现,闭包将可变引用引用到捕获的变量,以及实现 Fn 的所有类型,例如 (safe) 函数指针 (因为 FnMutFn 的特征)。 另外,对于任何实现 FnMutF 类型,&mut F 也实现 FnMut

由于 FnOnceFnMut 的 super trait,因此可以在期望 FnOnce 的地方使用 FnMut 的任何实例,并且由于 FnFnMut 的子特性,因此可以在预期 FnMut 的地方使用 Fn 的任何实例。

当您想接受类似函数类型的参数并需要反复调用它,同时允许其改变状态时,请使用 FnMut 作为绑定。 如果您不希望参数改变状态,请使用 Fn 作为绑定; 如果不需要重复调用,请使用 FnOnce

有关此主题的更多信息,请参见 Rust 编程语言 中关于闭包的章节。

还要注意的是 Fn traits 的特殊语法 (例如 Fn(usize, bool) -> usize)。对此技术细节感兴趣的人可以参考 Rustonomicon 中的相关部分

示例

调用可变捕获闭包

let mut x = 5;
{
    let mut square_x = || x *= x;
    square_x();
}
assert_eq!(x, 25);

使用 Fn 参数:

fn call_with_one<F>(func: F) -> usize
    where F: Fn(usize) -> usize {
    func(1)
}

let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);

2.3 - trait core::ops::Fn

Fn 由只对捕获的变量进行不可变引用或根本不捕获任何内容

源码

Trait core::ops::Fn 的定义如下:

pub trait Fn<Args>: FnMut<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

官方文档

https://doc.rust-lang.org/core/ops/trait.Fn.html

采用不可变接收者的调用运算符的版本。

Fn 的实例可以在不改变状态的情况下重复调用。

请勿将此 trait (Fn) 与 函数指针 (fn) 混淆。

Fn 由闭包自动实现,闭包只对捕获的变量进行不可变引用或根本不捕获任何内容,还有 (安全) 函数指针 (有一些警告,请参见其文档以获取更多详细信息)。

此外,对于实现 Fn 的任何类型 F&F 也实现了 Fn

由于 FnMutFnOnce 都是 Fn 的 supertraits,因此 Fn 的任何实例都可以用作参数,其中需要 FnMutFnOnce

当您要接受类似函数类型的参数并且需要反复调用且不改变状态 (例如,同时调用它) 时,请使用 Fn 作为绑定。 如果不需要严格的要求,请使用 FnMutFnOnce 作为界限。

有关此主题的更多信息,请参见 Rust 编程语言 中关于闭包的章节。

还要注意的是 Fn traits 的特殊语法 (例如 Fn(usize, bool) -> usize)。对此技术细节感兴趣的人可以参考 Rustonomicon 中的相关部分

示例

调用一个闭包:

let square = |x| x * x;
assert_eq!(square(5), 25);

使用 Fn 参数:

fn call_with_one<F>(func: F) -> usize
    where F: Fn(usize) -> usize {
    func(1)
}

let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);