笔记-Rust函数

206 阅读4分钟

函数

基本知识

对于一些重复执行的代码,可以将其定义成一个函数,方便调用。

函数 = 函数签名 + 函数体

按值传递的参数使用 mut 关键字

fn modify(mut v: Vec<u32>) -> Vec<u32> {
    v.push(42);
    v
}

fn main() {
    let v = vec![1, 2, 3];
    let v = modify(v);
    println!("v = {:?}", v);
}

按引用传递参数时的 mut 的用法

fn modify(v: &mut Vec<u32>) {
    v.push(42);
}

fn main() {
    let mut v = vec![1, 2, 3];
    modify(&mut v);
    println!("v = {:?}", v);
}

函数屏蔽

变量屏蔽(variable shadow):当声明变量绑定后,如果再次声明同名的变量绑定,则之前的变量绑定会被屏蔽。

函数不能多次定义:否则报错 error[E0428]: the name `xxx` is defined multiple times。

但是,可以通过显式地使用花括号将同名的函数分割到不同的作用域中。默认的函数定义只在当前作用域内有效,会屏蔽作用域外的同名函数。

fn f() {
    println!("1");
}

fn main() {
    f(); // 2
    {
        f(); // 3
        fn f() {
            println!("3");
        }
    }
    f(); // 2
    fn f() {
        println!("2");
    }
}

函数参数模式匹配

函数中的参数等价于一个隐式的 let 绑定,而 let 绑定本事是一个模式匹配的行为。所以函数参数也支持模式匹配。

#[derive(Debug)]
struct S {
    i: i32,
}

fn f(ref s: S) {
    println!("{:p}", s); // 需要 ref 修饰
    println!("{:?}", s);
}

fn main() {
    let s = S { i: 42 };
    f(s);
    // println!("{:?}", s);
}

使用通配符忽略参数:

fn foo(_ : i32) {
    //...
}

fn main() {
    foo(3);
}

Rust 中的 let 语句可以通过模式匹配解构元组 (Tuple),函数参数也可以。

// 函数利用模式匹配来解构元组
fn foo((x, y): (&str, i32)) {
    println!("x = {}", x); // x = xiaoming
    println!("y = {}", y); // y = 101
}

fn main() {
    foo(("xiaoming", 101));
}

函数返回值

Rust 中的函数只能有唯一的返回值。没有返回值的情况,实际上相当于返回了一个单元值 ()。如果有多个返回值,可以使用元组类型来返回。

fn foo(x: isize, y: isize) -> (isize, isize) {
    (x + y, x - y)            // 可以使用 return 来返回。也可以不加分号,默认返回。
}

fn main() {
    let (a, b) = foo(5, 8);
    println!("a = {}, b = {}", a, b);  // a = 13, b = -3
}

泛型函数

通用类型支持。

use std::ops::Mul;

fn square<T: Mul<T, Output = T>>(x: T, y: T) -> T {
    x * y
}

fn main() {
    // println!("square(3, 6) = {}", square(3, 6)); // square(3, 6) = 18
    let a = square(37.2, 41.1); // f64
    let b = square::<u32>(10, 6); // 若使用 u32,则参数不能是负数,不能是浮点数
    let c = square::<f32>(16f32, 26.3); // 指定 f32 类型
}

方法与函数

Rust 中的方法和函数是有区别的。

  • 方法代表某个实例对象的行为,必须关联一个方法接受者,然后使用名字来进行调用。

  • 函数只是一段简单的代码,它可以通过名字来进行调用。

struct User {
    name: &'static str,
    avatar_url: &'static str,
}

impl User {
    fn show(&self) {
        println!("name = {}", self.name);
        println!("avatar = {}", self.avatar_url);
    }
}

fn main() {
    let u1 = User {
        name: "lee",
        avatar_url: "http://avatar.com/lee",
    };
    u1.show(); // 方式1
    User::show(&u1); // 方式2,使用函数的形式,调用方法
}

高阶函数

在计算机科学中,高阶函数是指以函数作为参数或返回值的函数。

这是函数式编程语言最基础的特性,Rust 语言也支持高姐函数,函数在 Rust 中是一等公民。

函数本身可以作为参数进行传递。

```
fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {
    op(a, b)
}

fn sum(a: i32, b: i32) -> i32 {
    a + b
}

// 乘积 也有乘积的意思
fn product(a: i32, b: i32) -> i32 {
    a * b
}

fn main() {
    let (a, b) = (2, 3);
    println!("math(sum, a, b) = {}", math(sum, a, b)); // math(sum, a, b) = 5
    println!("math(product, a, b) = {}", math(product, a, b)); // math(product, a, b) = 6
}
```

实现上述功能的基础在于 Rust 支持函数指针。函数指针,是指向函数的指针,其值为函数的地址。

fn hello() {
    println!("hello function pointer");
}

fn main() {
    // fn_ptr 是函数指针类型 (Fn-Pointer Type),可以打印指针地址
    let fn_ptr: fn() = hello;                
    println!("fn_ptr = {:p}", fn_ptr);

    // other_fn 的实际类型是 fn() {hello},这实际是 函数类型(Fn-Item Type)
    let other_fn = hello;
    // println!("other_fn = {:p}", other_fn);  // 非函数指针,这样会报错

    hello();
    other_fn();

    fn_ptr();
    (fn_ptr)()
}

对于函数指针类型,使用 type 关键字定义别名,能提升代码的可读性

type MathOp = fn(i32, i32) -> i32;

fn math(op: MathOp, a: i32, b: i32) -> i32 {
    println!("{:p}", op);
    op(a, b)
}

将函数作为返回值

type MathOp = fn(i32, i32) -> i32;

fn math(op: &str) -> MathOp {
    fn sum(a: i32, b: i32) -> i32 {
        a + b
    }

    // 乘积 也有乘积的意思
    fn product(a: i32, b: i32) -> i32 {
        a * b
    }

    match op {
        "sum" => sum,
        "product" => product,
        _ => panic!("unknown method name: {}", op),
    }
}

fn main() {
    let (a, b) = (2, 6);

    let sum = math("sum");
    let product = math("product");
    println!("sum({}, {}) = {}", a, b, sum(a, b));
    println!("product({}, {}) = {}", a, b, product(a, b));

    let div = math("div"); // panic
}

Rust 不允许 返回的 fn 函数捕捉外部环境变量,若有需要,可以使用闭包来代替。

type MathOp = fn(i32, i32) -> i32;

fn math(out_num: i32) -> MathOp {
    fn sum(a: i32, b: i32) -> i32 {
        a + b + out_num  //  can't capture dynamic environment in a fn item。 help: use the `|| { ... }` closure form instead
    }

    return sum;
}