学习 Rust 的第四天:基本编程概念学习

发布于:2024-04-17 ⋅ 阅读:(23) ⋅ 点赞:(0)

嘿,大家好!

欢迎来到 Rust 学习的第四天,我参考的是 Steve Klabnik 的《Rust 编程语言》一书。昨天我们做了一个 [猜数字游戏](https://blog.csdn.net/Tory2/article/details/137764707?spm=1001.2014.3001.5501),今天我们将深入了解常见的编程概念,比如:

  • 变量
  • 常量
  • 局部变量
  • 数据类型
  • 函数

那么,我们就开始吧?

变量

用通俗的话说,变量就像是存储值的容器。我们快速看一下它们的特点:

  • 变量可以存储不同类型的值,比如:数字、文本或复杂结构
  • 每个变量都有一个名称
  • 在 Rust 中,默认情况下变量是不可变的,意味着一旦设置了值就不能更改,要使变量可变,使用 mut 关键字
  • 你可以明确定义变量将存储的值的类型,但这是可选的
  • 变量有限定的作用域,意味着它们只能在代码的某个特定部分访问

例子:

fn main(){  
  let x = 10; // 不可变变量  
  let mut y = 15; // 可变变量  
  println!("X = {}",x);  
  println!("Y = {}",y);  
  
  y = 2;  
  println!("X = {}",x);  
  println!("Y = {}",y);  
}

常量

与不可变变量类似,常量是不允许更改的值,但有一些区别:

  • 常量是固有不可变的,不能使用 mut 关键字
  • 它们在编译时初始化
  • 常量具有全局作用域,可以从程序的任何地方访问
  • 常量需要类型定义

例子:

const VALUE_OF_PI: u32 = 3.1418;

局部变量

就像昨天我们看到的那样,通过遮盖,你可以声明与之前变量相同名称的新变量,新变量将会 覆盖 前一个变量。

例子:

fn main(){  
  let x = 10;  
  let x = x - 7;  
  {  
    let x = x * 2;  
    println!("内部作用域中的 X = {}",x);  
  }  
  println!("外部作用域中的 X = {}",x);  
}

输出:

$ cargo run  
内部作用域中的 X = 6  
外部作用域中的 X = 3

数据类型

数据类型可以进一步分为两组:标量和复合数据类型。

标量数据类型

标量类型表示一个值,Rust 有 4 种主要标量数据类型:

整数

整数是没有分数部分的数字,我们昨天在猜数字游戏中使用了它,u32 表示无符号 32 位整数

其他类型的整数包括:

有符号的:
  • 有符号整数和无符号整数的区别在于数字是否需要有一个 符号,或者它是否只能是正数
  • 每个有符号整数可以存储从 -(2^(n — 1)) 到 (2^(n — 1)) — 1 的数字,其中 n 是变体使用的位数
  • 例如:i8、i16、i32、i64、i128
无符号的:
  • 无符号数总是正数。
  • 每个无符号整数可以存储从 0 到 2^n — 1 的数字
  • 例如:u8、u16、u32、u64、u128

此外还有 isizeusize,根据程序运行的计算机架构而定。如果程序运行在 32 位架构上,则是 32 位,如果程序运行在 64 位架构上,则是 64 位

整数溢出:
  • 计算机使用固定数量的位表示整数,确定它们可以覆盖的范围。
  • 在 Rust 中,当算术操作的结果超过数据类型可以表示的最大值时(溢出),它会绕回到最小值并继续计数。
整数溢出示例:

如果你使用的是 8 位整数,范围是从 0 到 255。如果你试图给 255 加 1,在正常的算术中,你会期望得到 256。然而,在 Rust 中,由于溢出,它会回到 0。

浮点数:

Rust 中的浮点数具有分数部分。与整数不同,浮点数始终是 有符号的。有两种类型的浮点数:f32f64,分别表示 32 位和 64 位大小。

例子:
fn main() {  
    // 使用 f32  
    let float_32: f32 = 3.14; // 一个 32 位浮点数  
    println!("f32 值: {}", float_32);  
  
    // 使用 f64  
    let float_64: f64 = 3.141592653589793; // 一个 64 位浮点数  
    println!("f64 值: {}", float_64);  
  
    // 执行算术操作  
    let result_f32 = float_32 * 2.0;  
    let result_f64 = float_64 * 2.0;  
  
    // 显示结果  
    println!("结果 (f32): {}", result_f32);  
    println!("结果 (f64): {}", result_f64);  
}

数字操作

Rust 支持基本的数学运算,如:

  • 加法
  • 减法
  • 乘法
  • 除法
  • 取余
例子:
fn main(){  
  let sum = 10+7;  
  let subtraction = 60-4;  
  let multiplication = 7 * 7;  
  let division = 25/5;  
  let modulus = 13%2;  
    
  println!("10 + 7 = {}",sum);  
  println!("60 - 4 = {}",subtraction);  
  println!("7 * 7 = {}",multiplication);  
  println!("25 / 5 = {}",division);  
  println!("13 % 2 = {}",modulus);  
}

布尔类型

与大多数编程语言一样,布尔类型大小为一个字节,可以是 truefalse

例子:
fn main(){  
  let t = true;  
  let f: bool = false;  
}

字符类型

Rust 的 char 类型是最原始的字母类型,类似于 C 中的 char

fn main(){  
  let z = 'Z';  
  let x: char = "X";  
}

侧面说明:字符用单引号括起来,而字符串用双引号括起来。

复合类型

复合类型可以在一个类型中存储多个值。它们通常是一组标量数据。

元组

在 Rust 中,元组类似于一个混合包,可以包含不同类型的项,允许你按特定顺序组织和组合它们。这是一种将多个值捆绑在一起的简单方法。

元组是不可变的。但是,如果它们声明为可变变量,元组内的元素可能是可变的。

语法
let var_name: (data_type1,data_type2...data_typeN) = (val1,val2...valN);  
  
// 这个元组中的值是可变的  
let mut var_name: (data_type1,data_type2...data_typeN) = (val1,val2...valN);
例子
let tup: (i32, f64, u16) = (500,3.141,9);
访问元组中的数据
索引:

索引是指通过其位置或键访问数据结构中的特定元素,如数组或元组。

要使用索引访问元组,我们可以这样做:

println!("{}",tuple_name.index);
例子:
fn main() {  
    // 创建一个元组  
    let my_tuple = (42, "hello", 3.14);  
  
    // 使用索引访问元素(从零开始)  
    let first_element = my_tuple.0;   // 访问第一个元素(42)  
    let second_element = my_tuple.1;  // 访问第二个元素("hello")  
    let third_element = my_tuple.2;   // 访问第三个元素(3.14)  
  
    // 打印访问的元素  
    println!("第一个元素: {}", first_element);  
    println!("第二个元素: {}", second_element);  
    println!("第三个元素: {}", third_element);  
}

数组

数组是另一种按顺序存储相同类型数据的方法。Rust 中的数组长度是固定的。

我们使用方括号 [] 作为逗号分隔列表来编写数组。

例子:
fn main(){  
  let a: [i32;5]= [1,2,3,4,5];  
}

我们还可以这样做:

fn main(){  
  let x = [6; 4]; // [6,6,6,6]  
}
访问数组元素:
fn main() {  
    let a: [u32; 6] = [1, 2, 3, 4, 5, 6];  
    let first: u32 = a[0];  
    let last: u32 = a[a.len() - 1];  
  
    println!("第一个元素 : {}", first); // 1  
    println!("最后一个元素 : {}", last); // 6  
}

函数

从技术上讲,函数的定义是,它是执行特定任务的自包含代码单元,接受输入并产生输出,增强代码结构和可重用性。

main 函数是 Rust 程序的入口点,同样我们可以使用 fn 关键字声明自定义函数来执行特定任务。

在 Rust 中,snake_case 是声明函数和变量名的约定。

例子
fn main(){  
  println!("主函数");  
  new_function();  
}  
  
fn new_function(){  
  println!("这是一个自定义函数");  
}
输出
$ cargo run  
主函数  
这是一个自定义函数

参数

通俗地说:参数是传递给函数的输入

技术上来说,参数是函数定义中的一个占位符,参数是你调用函数时提供给函数的实际值或数据。它们与函数中定义的参数的类型和顺序相匹配。

虽然人们往往会将这些词混用。

例子
fn main(){  
    println!("主函数");  
    addition(5,9);  
}  
fn addition(x: i32,y: i32){  
    println!("{} + {} 的和 = {}",x,y,x+y);  
}
输出
$ cargo run  
主函数  
5 + 9 的和 = 14
语句和表达式

语句是执行特定任务的指令,而表达式则评估为一个值。

例子
fn main(){  
  let y = 6; // 语句  
  let x = 5; // 语句  
  
  let result = x+y; // 这里的 "x+y" 是一个表达式  
}

带有返回值的函数

函数可以在调用时返回一个值。我们必须使用箭头 ->

明函数的返回类型。

在 Rust 中,函数的最终表达式是函数的返回值。

例子
fn main(){  
    println!("主函数");  
    let sum = addition(5,9);  
    println!("函数的结果 : {}", sum);  
}  
fn addition(x: i32,y: i32) -> i32 {  
    x+y // 返回语句  
}

在 Rust 中,返回语句末尾的分号的缺失表示它是要返回的值,这样代码更加明确,减少了冗余。

结论

今天就讲到这里,明天我会深入了解 Rust 中的控制流程。