• 9. 函数


    • Rust中函数的定义位置是没有限制的,不必先声明再定义。
    • 本章原文
    // 和 C/C++ 不一样,Rust 的函数定义位置是没有限制的
    fn main() {
        // 我们可以在这里使用函数,后面再定义它
        fizzbuzz_to(100);
    }
    
    // 一个返回布尔值的函数
    fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
        // 边界情况,提前返回
        if rhs == 0 {
            return false;
        }
    
        // 这是一个表达式,可以不用 `return` 关键字
        lhs % rhs == 0
    }
    
    // 一个 “不” 返回值的函数。实际上会返回一个单元类型 `()`。
    fn fizzbuzz(n: u32) -> () {
        if is_divisible_by(n, 15) {
            println!("fizzbuzz");
        } else if is_divisible_by(n, 3) {
            println!("fizz");
        } else if is_divisible_by(n, 5) {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
    
    // 当函数返回 `()` 时,函数签名可以省略返回类型
    fn fizzbuzz_to(n: u32) {
        for n in 1..=n {
            fizzbuzz(n);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    方法

    • 方法在结构体中常常使用,使用impl进行定义,同时又由关键字self进行访问。
    • 有两种方法的定义方法,一种是静态方法,一种是实例方法,调用方式,静态方法使用"::“调用,实例方法使用”."调用
    • 实例方法的三种调用方式:
    &selfself&mut self
    调用之后仍然可用调用之后如果释放了,不可用调用之后可以改变对像的值
    struct Point {
        x: f64,
        y: f64,
    }
    
    // 实现的代码块,`Point` 的所有方法都在这里给出
    impl Point {
        // 这是一个静态方法(static method)
        // 静态方法不需要被实例调用
        // 这类方法一般用作构造器(constructor)
        fn origin() -> Point {
            Point { x: 0.0, y: 0.0 }
        }
    
        // 另外一个静态方法,需要两个参数:
        fn new(x: f64, y: f64) -> Point {
            Point { x: x, y: y }
        }
    }
    
    struct Rectangle {
        p1: Point,
        p2: Point,
    }
    
    impl Rectangle {
        // 这是一个实例方法(instance method)
        // `&self` 是 `self: &Self` 的语法糖(sugar),其中 `Self` 是方法调用者的
        // 类型。在这个例子中 `Self` = `Rectangle`
        fn area(&self) -> f64 {
            // `self` 通过点运算符来访问结构体字段
            let Point { x: x1, y: y1 } = self.p1;
            let Point { x: x2, y: y2 } = self.p2;
    
            // `abs` 是一个 `f64` 类型的方法,返回调用者的绝对值
            ((x1 - x2) * (y1 - y2)).abs()
        }
    
        fn perimeter(&self) -> f64 {
            let Point { x: x1, y: y1 } = self.p1;
            let Point { x: x2, y: y2 } = self.p2;
    
            2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
        }
    
        // 这个方法要求调用者是可变的
        // `&mut self` 为 `self: &mut Self` 的语法糖
        fn translate(&mut self, x: f64, y: f64) {
            self.p1.x += x;
            self.p2.x += x;
    
            self.p1.y += y;
            self.p2.y += y;
        }
    }
    
    // `Pair` 拥有资源:两个堆分配的整型
    struct Pair(Box<i32>, Box<i32>);
    
    impl Pair {
        // 这个方法会 “消耗” 调用者的资源
        // `self` 为 `self: Self` 的语法糖
        fn destroy(self) {
            // 解构 `self`
            let Pair(first, second) = self;
    
            println!("Destroying Pair({}, {})", first, second);
    
            // `first` 和 `second` 离开作用域后释放
        }
    }
    
    fn main() {
        let rectangle = Rectangle {
            // 静态方法使用双冒号调用
            p1: Point::origin(),
            p2: Point::new(3.0, 4.0),
        };
    
        // 实例方法通过点运算符来调用
        // 注意第一个参数 `&self` 是隐式传递的,亦即:
        // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
        println!("Rectangle perimeter: {}", rectangle.perimeter());
        println!("Rectangle area: {}", rectangle.area());
    
        let mut square = Rectangle {
            p1: Point::origin(),
            p2: Point::new(1.0, 1.0),
        };
    
        // 报错! `rectangle` 是不可变的,但这方法需要一个可变对象
        //rectangle.translate(1.0, 0.0);
        // 试一试 ^ 去掉此行的注释
    
        // 正常运行!可变对象可以调用可变方法
        square.translate(1.0, 1.0);
    
        let pair = Pair(Box::new(1), Box::new(2));
    
        pair.destroy();
    
        // 报错!前面的 `destroy` 调用 “消耗了” `pair`
        //pair.destroy();
        // 试一试 ^ 将此行注释去掉
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    闭包

    • 闭包产生的类型就叫做闭包类型,和引用类型不同。
    • 闭包常常在临时使用时使用。
    • 建议使用闭包的时候尽量标注类型。
    • 闭包和函数使用方法差不多,但闭包能干的事情不如函数多。
    fn main() {
        // 通过闭包和函数分别实现自增。
        // 译注:下面这行是使用函数的实现
        fn  function            (i: i32) -> i32 { i + 1 }
    
        // 闭包是匿名的,这里我们将它们绑定到引用。
        // 类型标注和函数的一样,不过类型标注和使用 `{}` 来围住函数体都是可选的。
        // 这些匿名函数(nameless function)被赋值给合适地命名的变量。
        let closure_annotated = |i: i32| -> i32 { i + 1 };
        let closure_inferred  = |i     |          i + 1  ;
    
        // 译注:将闭包绑定到引用的说法可能不准。
        // 据[语言参考](https://doc.rust-lang.org/beta/reference/types.html#closure-types)
        // 闭包表达式产生的类型就是 “闭包类型”,不属于引用类型,而且确实无法对上面两个
        // `closure_xxx` 变量解引用。
    
        let i = 1;
        // 调用函数和闭包。
        println!("function: {}", function(i));
        println!("closure_annotated: {}", closure_annotated(i));
        println!("closure_inferred: {}", closure_inferred(i));
    
        // 没有参数的闭包,返回一个 `i32` 类型。
        // 返回类型是自动推导的。
        let one = || 1;
        println!("closure returning one: {}", one());
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    闭包的捕获

    • 闭包的捕获可以通过以下方式捕获变量,或者使用move(移动)变量。
    • 默认状态下是使用借用调用,也可以添加关键字mut或者move直接获取。
    引用可变引用
    &T&mut TT
    fn main() {
        use std::mem;
    
        let color = String::from("green");
    
        // 这个闭包打印 `color`。它会立即借用(通过引用,`&`)`color` 并将该借用和
        // 闭包本身存储到 `print` 变量中。`color` 会一直保持被借用状态直到
        // `print` 离开作用域。
        //
        // `println!` 只需传引用就能使用,而这个闭包捕获的也是变量的引用,因此无需
        // 进一步处理就可以使用 `println!`。
        let print = || println!("`color`: {}", color);
    
        // 使用借用来调用闭包 `color`。
        print();
    
        // `color` 可再次被不可变借用,因为闭包只持有一个指向 `color` 的不可变引用。
        let _reborrow = &color;
        print();
    
        // 在最后使用 `print` 之后,移动或重新借用都是允许的。
        let _color_moved = color;
    
        let mut count = 0;
        // 这个闭包使 `count` 值增加。要做到这点,它需要得到 `&mut count` 或者
        // `count` 本身,但 `&mut count` 的要求没那么严格,所以我们采取这种方式。
        // 该闭包立即借用 `count`。
        //
        // `inc` 前面需要加上 `mut`,因为闭包里存储着一个 `&mut` 变量。调用闭包时,
        // 该变量的变化就意味着闭包内部发生了变化。因此闭包需要是可变的。
        let mut inc = || {
            count += 1;
            println!("`count`: {}", count);
        };
    
        // 使用可变借用调用闭包
        inc();
    
        // 因为之后调用闭包,所以仍然可变借用 `count`
        // 试图重新借用将导致错误
        // let _reborrow = &count;
        // ^ 试一试:将此行注释去掉。
        inc();
    
        // 闭包不再借用 `&mut count`,因此可以正确地重新借用
        let _count_reborrowed = &mut count;
    
        // 不可复制类型(non-copy type)。
        let movable = Box::new(3);
    
        // `mem::drop` 要求 `T` 类型本身,所以闭包将会捕获变量的值。这种情况下,
        // 可复制类型将会复制给闭包,从而原始值不受影响。不可复制类型必须移动
        // (move)到闭包中,因而 `movable` 变量在这里立即移动到了闭包中。
        let consume = || {
            println!("`movable`: {:?}", movable);
            mem::drop(movable);
        };
    
        // `consume` 消耗了该变量,所以该闭包只能调用一次。
        consume();
        //consume();
        // ^ 试一试:将此行注释去掉。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 在闭包没有调用完成的过程中不能使用变量转移引用。
    • 使用move会强制获取所有权,原来的闭包将不能使用。
    fn main() {
        // `Vec` 在语义上是不可复制的。
        let haystack = vec![1, 2, 3];
    
        let contains = move |needle| haystack.contains(needle);
    
        println!("{}", contains(&1));
        println!("{}", contains(&4));
    
        //println!("There're {} elements in vec", haystack.len());
        // ^ 取消上面一行的注释将导致编译时错误,因为借用检查不允许在变量被移动走
        // 之后继续使用它。
    
        // 在闭包的签名中删除 `move` 会导致闭包以不可变方式借用 `haystack`,因此之后
        // `haystack` 仍然可用,取消上面的注释也不会导致错误。
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    闭包作为输入参数

    • 闭包在作为输入参数的时候需要指出完整的闭包类型,通过使用下面的trait来指定:
    FnFnMutFnOnce
    表示捕获方式为通过引用(&T)的闭包表示捕获方式为通过可变引用(&mut T)的闭包表示捕获方式通过值(T)的闭包
    • 闭包在使用的过程中,编译器使用以限制最多的方式捕获
    • 例如用一个类型说明为 FnOnce 的闭包作为参数。这说明闭包可能采取 &T&mut TT 中的一种捕获方式,但编译器最终是根据所捕获变量在闭包里的使用情况决定捕获方式。
    • 这是因为如果能以移动的方式捕获变量,则闭包也有能力使用其他方式借用变量。注意反过来就不再成立:如果参数的类型说明是 Fn,那么不允许该闭包通过 &mut TT 捕获变量。
    // 该函数将闭包作为参数并调用它。
    fn apply<F>(f: F) where
        // 闭包没有输入值和返回值。
        F: FnOnce() {
        // ^ 试一试:将 `FnOnce` 换成 `Fn` 或 `FnMut`。
    
        f();
    }
    
    // 输入闭包,返回一个 `i32` 整型的函数。
    fn apply_to_3<F>(f: F) -> i32 where
        // 闭包处理一个 `i32` 整型并返回一个 `i32` 整型。
        F: Fn(i32) -> i32 {
    
        f(3)
    }
    
    fn main() {
        use std::mem;
    
        let greeting = "hello";
        // 不可复制的类型。
        // `to_owned` 从借用的数据创建有所有权的数据。
        let mut farewell = "goodbye".to_owned();
    
        // 捕获 2 个变量:通过引用捕获 `greeting`,通过值捕获 `farewell`。
        let diary = || {
            // `greeting` 通过引用捕获,故需要闭包是 `Fn`。
            println!("I said {}.", greeting);
    
            // 下文改变了 `farewell` ,因而要求闭包通过可变引用来捕获它。
            // 现在需要 `FnMut`。
            farewell.push_str("!!!");
            println!("Then I screamed {}.", farewell);
            println!("Now I can sleep. zzzzz");
    
            // 手动调用 drop 又要求闭包通过值获取 `farewell`。
            // 现在需要 `FnOnce`。
            mem::drop(farewell);
        };
    
        // 以闭包作为参数,调用函数 `apply`。
        apply(diary);
    
        // 闭包 `double` 满足 `apply_to_3` 的 trait 约束。
        let double = |x| 2 * x;
    
        println!("3 doubled: {}", apply_to_3(double));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    类型匿名

    • 使用闭包作为函数参数要求闭包是泛型的。
    // `F` 必须是泛型的。
    fn apply<F>(f: F) where
        F: FnOnce() {
        f();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 闭包定义时,编译器会隐式创建一个匿名类型的结构体,来储存闭包捕获的变量,同时为这个未知类型的结构体实现函数功能,通过FnFnMutFnOnce中的三种trait中的一种。
    • 但是使用闭包作为参数是必须指定trait类型是三种中的哪一个,来进行限制。
    // `F` 必须为一个没有输入参数和返回值的闭包实现 `Fn`,这和对 `print` 的
    // 要求恰好一样。
    fn apply<F>(f: F) where
        F: Fn() {
        f();
    }
    
    fn main() {
        let x = 7;
    
        // 捕获 `x` 到匿名类型中,并为它实现 `Fn`。
        // 将闭包存储到 `print` 中。
        let print = || println!("{}", x);
    
        apply(print);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输入参数

    • 某个闭包作为参数,则这个函数也可以使用满足闭包约束(Fn、FnOnce、FnMut)的函数作为参数。
    // 定义一个函数,可以接受一个由 `Fn` 限定的泛型 `F` 参数并调用它。
    fn call_me<F: Fn()>(f: F) {
        f();
    }
    
    // 定义一个满足 `Fn` 约束的封装函数(wrapper function)。
    fn function() {
        println!("I'm a function!");
    }
    
    fn main() {
        // 定义一个满足 `Fn` 约束的闭包。
        let closure = || println!("I'm a closure!");
        
        call_me(closure);
        call_me(function);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    作为输出参数

    • 闭包可以作为输入参数,但是作为输出参数只能使用impl trait,因为输出参数只能返回具体值(非泛型)。
    • 这三个trait便是Fn、FnOnce、FnMut。
    • 同时必须使用move关键字,(因为这表明这些闭包是使用具体值进行,而函数在退出时也需要清空所有的引用,如果不使用会造成无效引用)
    fn create_fn() -> impl Fn() {
        let text = "Fn".to_owned();
    
        move || println!("This is a: {}", text)
    }
    
    fn create_fnmut() -> impl FnMut() {
        let text = "FnMut".to_owned();
    
        move || println!("This is a: {}", text)
    }
    
    fn create_fnonce() -> impl FnOnce() {
        let text = "FnOnce".to_owned();
        
        move || println!("This is a: {}", text)
    }
    
    fn main() {
        let fn_plain = create_fn();
        let mut fn_mut = create_fnmut();
        let fn_once = create_fnonce();
    
        fn_plain();
        fn_mut();
        fn_once();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Iterator::any

    • 这里要注意的是any函数的签名,返回值是bool,但是在函数内部,变量是通过值传递给闭包,这个是迭代器对应元素的类型。输入值最多只能被修改不能被消耗。(这也与之前编译器以限制最多的方法进行设限相对应)

    Iterator::find

    • 注意find函数会将迭代器的引用传递给闭包。所以会出现解构((&&x) x ==2)之类的。
    fn main() {
        let vec1 = vec![1, 2, 3];
        let vec2 = vec![4, 5, 6];
    
        // 对 vec1 的 `iter()` 举出 `&i32` 类型。
        let mut iter = vec1.iter();
        // 对 vec2 的 `into_iter()` 举出 `i32` 类型。
        let mut into_iter = vec2.into_iter();
    
        // 对迭代器举出的元素的引用是 `&&i32` 类型。解构成 `i32` 类型。
        // 译注:注意 `find` 方法会把迭代器元素的引用传给闭包。迭代器元素自身
        // 是 `&i32` 类型,所以传给闭包的是 `&&i32` 类型。
        println!("Find 2 in vec1: {:?}", iter     .find(|&&x| x == 2));
        // 对迭代器举出的元素的引用是 `&i32` 类型。解构成 `i32` 类型。
        println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));
    
        let array1 = [1, 2, 3];
        let array2 = [4, 5, 6];
    
        // 对数组的 `iter()` 举出 `&i32`。
        println!("Find 2 in array1: {:?}", array1.iter()     .find(|&&x| x == 2));
        // 对数组的 `into_iter()` 通常举出 `&i32``。
        println!("Find 2 in array2: {:?}", array2.into_iter().find(|&x| x == 2));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    高阶函数

    • 这种高阶函数是函数中输入一个或者多个函数,然后产生一个更厉害的函数。这是函数式风格编程的特点。
    fn is_odd(n: u32) -> bool {
        n % 2 == 1
    }
    
    fn main() {
        println!("Find the sum of all the squared odd numbers under 1000");
        let upper = 1000;
    
        // 命令式(imperative)的写法
        // 声明累加器变量
        let mut acc = 0;
        // 迭代:0,1, 2, ... 到无穷大
        for n in 0.. {
            // 数字的平方
            let n_squared = n * n;
    
            if n_squared >= upper {
                // 若大于上限则退出循环
                break;
            } else if is_odd(n_squared) {
                // 如果是奇数就计数
                acc += n_squared;
            }
        }
        println!("imperative style: {}", acc);
    
        // 函数式的写法
        let sum_of_squared_odd_numbers: u32 =
            (0..).map(|n| n * n)             // 所有自然数取平方
                 .take_while(|&n| n < upper) // 取小于上限的
                 .filter(|&n| is_odd(n))     // 取奇数
                 .fold(0, |sum, i| sum + i); // 最后加起来
        println!("functional style: {}", sum_of_squared_odd_numbers);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    发散函数

    • 发散函数没有返回值,使用!标记,这是空类型
    • 这种类型无法实例化,因为可能具有的所有可能值集合为空。和()类型不同,后者只有一个可能的值。
    fn foo() -> ! {
        panic!("This call never returns.");
    }
    
    • 1
    • 2
    • 3
    • 下面的例子可以正常返回,说明了空类型和()类型的不同。
    fn some_fn() {
        ()
    }
    
    fn main() {
        let a: () = some_fn();
        println!("This function returns and you can see this line.")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 这个例子不能返回,因为是发散函数
    #![feature(never_type)]
    
    fn main() {
        let x: ! = panic!("This call never returns.");
        println!("You will never see this line!");
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • match分支中使用发散函数,因为continue不会返回任何值。
    fn main() {
        fn sum_odd_numbers(up_to: u32) -> u32 {
            let mut acc = 0;
            for i in 0..up_to {
                // 注意这个 match 表达式的返回值必须为 u32,
                // 因为 “addition” 变量是这个类型。
                let addition: u32 = match i%2 == 1 {
                    // “i” 变量的类型为 u32,这毫无问题。
                    true => i,
                    // 另一方面,“continue” 表达式不返回 u32,但它仍然没有问题,
                    // 因为它永远不会返回,因此不会违反匹配表达式的类型要求。
                    false => continue,
                };
                acc += addition;
            }
            acc
        }
        println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 这也是永远循环(如 loop {})的函数(如网络服务器)或终止进程的函数(如 exit())的返回类型。
  • 相关阅读:
    论文阅读:CenterFormer: Center-based Transformer for 3D Object Detection
    20天深度复习JavaSE的详细笔记(二十)——XML、解析、设计模式等
    CDN工作原理
    Redis之五大基础数据结构深入、应用场景
    题解 P8757 [蓝桥杯 2021 省 A2] 完美序列
    基于WEB的网上购物系统的设计与实现
    VSCode2022配置Python3.9.6教程
    在word中将latex格式的公式转化为带有编号的mathtype公式
    尚硅谷wepack课程学习笔记
    MATLAB环境下基于深层小波散射网络的纹理图像分类方法
  • 原文地址:https://blog.csdn.net/SQZXR/article/details/127833595