Rust学习笔记(二)|变量、函数与控制流

本篇文章包含的内容

  • [1 变量与常量](#1 变量与常量)
  • [2 类型](#2 类型)
    • [2.1 标量类型](#2.1 标量类型)
    • [2.2 复合类型](#2.2 复合类型)
  • [3 函数](#3 函数)
  • [4 控制流](#4 控制流)
    • [4.1 分支](#4.1 分支)
    • [4.2 循环](#4.2 循环)

1 变量与常量

在Rust中,使用let关键字声明一个变量,变量默认是不可变的。如果要声明可变变量,需要使用mut关键字将其声明为可变变量。

rust 复制代码
let x = 1;
x = 2;			// 默认不可变,非法
let x = 2;		// shadow,合法

let mut y = 1;
y = 2;

声明常量需要使用const关键字,并且在声明时必须指明类型。它甚至可以声明在全局部位,命名需要满足一定的规则:字母全部大写,单词之间用下划线隔开。

rust 复制代码
const MAX_POINT : u32 = 100_000;

常量一定是不可变的,所以不需要mut关键字声明,但是它与默认不可变的let声明的变量有区别。它的赋值结果只能是常量表达式,不能是函数的返回值或者运行时才可以获得的值。

shadowing(隐藏)是Rust的一个重要特性,它允许新声明的变量的变量名替代旧变量。这在很大程度上提高了程序的可读性。被shadow前后的变量类型可以不同。

rust 复制代码
let spaces = "    ";			// str
let spaces = spaces.len();		// usize,这里返回 4

2 类型

2.1 标量类型

Rust中有四种标量类型(老四样):

  • 整数:默认i32
  • 浮点:默认f64
  • 布尔:只有两个值,truefalse,占一个字节。
  • 字符:Unicode标量值,占用4个字节,可以表示中日韩字符,emoji表情等。

整数类型具体可由下表表示。其中isizeusize由操作系统决定,如果操作系统是64位,则isize相当于i64usize相当于u64

整数的表示与C/C++类似,甚至可以添加下划线以提高可读性。例如10_00000xA00b1100_0011、甚至可以表示为byte类型(u8 only):b'A'。除了byte类型外,其他整数都可以使用类型后缀,例如57u8。如果整数溢出,Rust仅会在debug编译模式下panic(恐慌),而在release版本中程序不会panic。

2.2 复合类型

Rust语言中有两种基础的复合类型,即元组(Tuple)和数组。元组用小括号表示,数组用中括号表示。元组中的每一个元素可以是不同类型,而数组中的所有元素都必须是同一种类型。无论是元组还是数组,都需要显式声明他们的类型。

下面是一个使用元组的例子,可以临时声明一个新的元组,例如(x, y, z)来结构获取旧元组中的每一个成员(Rust会自动推断xyz的类型)。

rust 复制代码
fn main() {
    let tup : (i32, i64, u8) = (1, 2, 3);

    let (x, y, z) = tup;    // destructure

    println!("{}, {}, {}", x, y, z);
    println!("{}, {}, {}", tup.0, tup.1, tup.2)
}

使用下面的方法定义一个数组,可以显式地制定数组元素的类型,也可以让Rust编译器自动推断所需的数组类型。如果访问的数组索引超出了数组的范围,编译可能不会报错,但是运行时会导致程序panic。

rust 复制代码
fn main() {
    let a = [1, 2, 3, 4 , 5];
    let b: [i32; 5] = [1, 2, 3, 4, 5];

    let mouths = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ];

    let arr = [1; 3];   // let arr = [1, 1, 1];

    println!("{} {}", a[0], b[1]);
}

如果想让数据存放在stack(栈)内存而不是heap(堆)内存上,或者想要保证有固定数量的元素,则可以使用数组。数组是Stack上分配的单个块的内存。数组是定长的,而与之对应可变长度的数组称为Vector(向量)。如果在实际编程中不知道需要使用数组还是Vector,那么一般都需要使用Vector。

3 函数

Rust函数的命名规范采用snake_case规范,即全部小写,单词之间用下划线分开。Rust中的函数定义顺序无需尊循C/C++的习惯,即函数必须先声明再使用,在Rust中,可以将函数的定义放在函数使用之后。函数参数的类型必须指明。

rust 复制代码
fn main() {
    another_func(3);
}

fn another_func(num: i32) {
    println!("Another function!");
    println!("the num is {}", num)
}

Rust是一个基于表达式的语言 。任何一个函数体(语句块,由花括号括起)都由一系列语句组成,可选地,可以由一个表达式结束,函数的返回值就是表达式的值(表达式会产生一个计算值)

与表达式对应的概念称为语句,一个语句相当于一个命令,语句没有返回值(它返回一个空的Tuple)。函数的定义也是一个语句,所以它不能当作值赋值给变量(废话),这一点和C/C++有很大不同(笔者认为相当于修复了C/C++的一个bug?)。函数的定义是语句,但是调用函数(宏)是一个表达式。

rust 复制代码
let x = (let y = 6);		// 非法

let y = {
	let x = 1;
	x + 2		// 不能加分号
};

println!("the value of y is {}", y);	// 3

对于需要返回值的函数,在->符号后声明函数返回值的类型,但是不能为返回值命名。在Rust中,函数的返回值就是函数最后一个表达式的值,如果需要提前返回,则可以使用 return 关键字,并指定一个值

rust 复制代码
fn main() {
    let x = add_five(4);

    println!("{}", x);		// 9
}

fn add_five(num: i32) -> i32 {
    num + 5
    
    // return num + 5;
}

4 控制流

4.1 分支

if表达式后需要添加一个条件,条件必须是bool类型(它不能是一个语句,这一点也与C/C++不同),与if相关联的代码称为分支(arm)。当程序中出现多个else if,则建议只用match语句重构代码。

rust 复制代码
fn main() {
    let x = 3;
    if x % 2 == 0 {
        println!("x can be devived by 2");
    } else if x % 3 == 0 {
        println!("x can be devived by 3");
    } else if x % 4 == 0 {
        println!("x can be devived by 4")
    }
}

if是一个表达式,所以它可以放在赋值号的右边。实现类似 ? : ;的效果。需要特别注意的是,ifelse后程序块的返回值的类型必须相同。

rust 复制代码
let condition = true;

let number = if condition { 5 } else { 6 };		// 5

4.2 循环

Rust中有三种循环:loopwhilefor。其使用方法与C/C++和Python比较类似,但是Rust也提供了一些其他的语法糖。

loop表达式中可以使用break语句打断循环,并且break语句后可以添加表达式的值来指定loop表达式的返回值:

rust 复制代码
fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("result: {}", result);		// 20
}

while循环使用较为简单,这里不赘述。

遍历集合元素一般使用for。Rust中同样存在迭代器的概念。注意,下面的例子中迭代器返回的是数组a的引用,如果直接写for element in a,则element将会被推断为i32类型,而不是&i32

rust 复制代码
fn main() {
    let a = [1, 2, 3, 4, 5];

    for element in a.iter() {
        println!("the value is {}", element);
    }
}

如果需要代码执行确定的次数,可以使用标准库提供的RangeRange可以通过开始数字和结束数字生成一个范围,但不包含结束数字。可以使用rev()方法获得反转的Range

rust 复制代码
fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }

    println!("LIFTOFF!");
}

*  原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、FPGA方面的学习笔记。*


相关推荐
饮浊酒7 分钟前
Python学习-----3.基础语法(2)
python·学习
Hello_Embed1 小时前
STM32HAL 快速入门(六):GPIO 输入之按键控制 LED
笔记·stm32·单片机·嵌入式硬件·学习
Source.Liu1 小时前
【unitrix数间混合计算】2.21 二进制整数加法计算(bin_add.rs)
rust
奶黄小甜包1 小时前
C语言零基础第16讲:内存函数
c语言·笔记·学习
ZeroToOneDev5 小时前
Java(泛型和JUnit)
java·开发语言·笔记
Source.Liu5 小时前
【unitrix数间混合计算】2.20 比较计算(cmp.rs)
rust
许野平5 小时前
Rust:构造函数 new() 如何进行错误处理?
开发语言·后端·rust
许野平5 小时前
Rust:专业级错误处理工具 thiserror 详解
rust·error·错误处理·result·thiserror