rust学习 -- 第四章 函数

49 阅读5分钟

第四章 函数

基础函数

声明函数使用fn关键字.函数可以有一系列的输入参数, 还有一个返回类型.

fn main()
{ }

Rust编写的可执行程序的入口就是fn main()函数.

fn add1(t: (i32: i32)) -> i32 {
    t.0 + t.1
}
​
fn add2((x,y) : (i32,i32)) -> i32 {
    x + y
}

函数的输入是一个tuple(i32, i32) 返回的是 i32值. 函数体内部是一个表达式, 这个表达式的值就是函数的返回值. 函数也可以不写返回类型,这时返回类型是unit().

函数的参数

parameters 函数定义时的那个参数,

arguments 函数调用时传进去的参数

函数定义时 参数的类型必须指定.

函数的返回值

  • ->符号后面声明函数返回值的类型,但是不可以为返回值命名.
  • rust中,默认返回值是函数体中最后一个表达式的值.
  • 如果要提前返回值,需要用return关键字,指定一个值.
fn five() -> i32
{
    5 // 没有分号,采用默认的返回形式
}
​
fn main(){
    let x = five();
}

在Rust中, 每一个函数都具有自己单独的类型, 但是这个类型可以自动转换到fn类型. 示例如下:

fn main () {
    // 先让func 指向 add1
    let mut func = add1;
    // 再重新赋值, 让func指向 add2
    func = add2;  // 这里会报错,因为两个函数的类型不符
}

虽然add1和add2有同样的参数类型和同样的返回值类型, 但它们是不同类型, 所以这里报错了. 修复方案是让func的类型为通用的fn类型即可:

// 写法一,用 as 类型转换
let mut func = add1 as fn((i32,i32))->i32;
// 写法二,用显式类型标记
let mut func : fn((i32,i32))->i32 = add1;

嵌套定义 Rust的函数体内也允许定义其他item, 包括静态变量、 常量、 函数、 trait、 类型、 模块等. 比如:

fn test_inner() {
    static INNER_STATIC: i64 = 42;
​
    // 函数内部定义的函数
    fn internal_incr(x: i64) -> i64 {
        x + 1
    } 
​
    struct InnerTemp(i64);
    impl InnerTemp {
        fn incr(&mut self) {
            self.0 = internal_incr(self.0);
        }
    } 
​
    // 函数体,执行语句
    let mut t = InnerTemp(INNER_STATIC);
    t.incr();
    println!("{}", t.0);
}

函数体中的语句和表达式

  1. 函数体由一系列语句组成,可选的由一个表达式结束.
  2. rust是一个基于表达式的语言.
  3. 语句是执行一些动作的指令.
  4. 表达式会计算产生一个值.
fn main(){
    let x = 5; // 语句,他没有一个返回值.
    let y = 6+7; // 6+7 是一个表达式,他会产生一个返回值
​
    let y = {
        let x = 1;
        x + 3
    }; // 这个块,也是一个表达式,其中x+3的值会作为返回值.
​
    let y = {
        let x = 1;
        x + 3// 当有x+3; 这个块就不会有返回值了,返回的是一个空的tuple,
    }; 
}

发散函数

Rust支持一种特殊的发散函数(Diverging functions) , 它的返回类型是感叹号! . 如果一个函数根本就不能正常返回, 那么它可以这样写:

fn diverges() -> ! {
    panic!("This function never returns!");
}

发散类型的最大特点就是, 它可以被转换为任意一个类型. 为什么需要返回这样的类型呢?

let p = if x { 
    panic!("error");
} else {
    100
};

if else 中要求类型相同, 那么 panic! 就应该返回和100 相同的类型,这就是 !类型的作用了.

在Rust中, 有以下这些情况永远不会返回, 它们的类型就是! .

  • panic! 以及基于它实现的各种函数/宏, 比如unimplemented! 、unreachable!
  • 死循环loop{};
  • 进程退出函数std::process::exit 以及类似的libc中的exec一类函数.

main函数

在大部分主流操作系统上, 一个进程开始执行的时候可以接受一系列的参数, 退出的时候也可以返回一个错误码.

Rust的设计稍微有点不一样, 传递参数和返回状态码都由单独的API来完成.

fn main () {
    for arg in std::env::args() {
        println!("Arg: {}", arg);
    }
    std::process::exit(0);  // 指定错误码
}
​
// 执行 test -opt1 opt2 -- opt3

读取环境变量, 可以用std::env::var() 以及std::env::vars()函数获得.

fn main() {
    for arg in std::env::args() {
        match std::env::var(&arg) {
            Ok(val) => println!("{}: {:?}", &arg, val),
            Err(e) => println!("couldn't find environment {}, {}", &arg, e),
        }
    }
    println!("All environment varible count {}", std::env::vars().count());
}

var() 函数可以接受一个字符串类型参数, 用于查找当前环境变量中是否存在这个名字的环境变量, vars() 函数不携带参数, 可以返回所有的环境变量.

此前, Rust的main函数只支持无参数、 无返回值类型的声明方式,即main函数的签名固定为:fn main() -> ()

Rust设计组扩展了main函数的签名,使它变成了一个泛型函数, 这个函数的返回类型可以是任何一个满足Terminationtrait约束的类型, 其中( ) 、 bool、 Result都是满足这个约束的, 它们都可以作为main函数的返回类型.

const fn

函数可以用const关键字修饰, 这样的函数可以在编译阶段被编译器执行, 返回值也被视为编译期常量.

cube函数接受数字参数, 它会返回一个数字, 而且这个返回值本身可以用于给一个const常量做初始化, const常量又可以当成一个常量数组的长度使用.

#![feature(const_fn)]const fn cube(num:usize) -> usize {
    num * num * num
}
​
fn main() {
    const DIM: usize = cube(2);
    const ARR: [i32; DIM] = [0; DIM];
​
    println!("{:?}", ARR);
}