跳转至

通用概念

创建项目

Bash
$ cargo new variable
     Created binary (application) `variable` package

3.1 变量与可变性

创建变量

  • 声明变量使用let 关键字
  • 默认情况下,变量是不可变的 (Immutable)
Rust
fn main() {
    println!("Hello, world!");

    let x=5;
    print!("The value of x is {}",x);

    x=6;//报错
    print!("The value of x is {}",x);

}

cannot assign twice to immutable variable

Bash
$ cargo run
   Compiling variable v0.1.0 (D:\Workplace\Rust\study\variable)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src\main.rs:7:5
  |
4 |     let x=5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
...
7 |     x=6;
  |     ^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variable` due to previous error
- 声明变量时,在变量前面加上 mut,就可以使变量可变

Rust
fn main() {
    println!("Hello, world!");

    let mut x=5;
    print!("The value of x is {}",x);

    x=6;//报错
    print!("The value of x is {}",x);

}
Bash
$ cargo run
   Compiling variable v0.1.0 (D:\Workplace\Rust\study\variable)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target\debug\variable.exe`
Hello, world!
The value of x is 5The value of x is 6

变量与常量

常量 (constant),常量在绑定值以后也是不可变的,但是它与不可变的变量有很多区别: - 不可以使用 mut,常量永远都是不可变的 - 声明常量使用 const 关键字,它的类型必须被标注 - 常量可以在任何作用域内进行声明,包括全局作用域 - 常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算出的值 - 在程序运行期间,常量在其声明的作用域内一直有效 - 命名规范:Rust 里常量使用全大写字母,每个单词之间用下划线分开,例如:- MAX_POINTS - 可对数字插入下划线增强可读性 - 例子: const MAX_POINTS:u32= 100_000;

Rust
const MAX_POINTS:u32=100_100;
fn main() {
    println!("MAX_POINTS={}",MAX_POINTS);
}
Bash
$ cargo run
   Compiling variable v0.1.0 (D:\Workplace\Rust\study\variable)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target\debug\variable.exe`
MAX_POINTS=100100

Shadowing(隐藏)

  • 可以使用相同的名字声明新的变量,新的变量就会 shadow(隐藏)之前声明的同名变量
    • 在后续的代码中这个变量名代表的就是新的变量
  • shadow 和把变量标记为 mut 是不一样的:
    • 如果不使用let 关键字,那么重新给非 mut 的变量赋值会导致编译时错误
    • 而使用 let 声明的同名新变量,也是不可变的
    • 使用 let 声明的同名新变量,它的类型可以与之前不同
Rust
fn main() {
    let x=5;
    let x=x+1;//6
    let x=x+2;//8
}
Rust
fn main() {
    let spaces="    ";
    let spaces=spaces.len();
    println!("{}",spaces);//4

    let mut x="    ";
    x=x.len();//报错

}

3.2 数据类型:变量类型

数据类型

  • 标量和复合类型
  • Rust 是静态编译语言,在编译时必须知道所有变量的类型
    • 基于使用的值,编译器通常能够推断出它的具体类型
    • 但如果可能的类型比较多(例如把 String 转为整数的 parse 方法) ,就必须添加类的标注,否则编译会报错(例子)
      Rust
      fn main() {
          let guess:u32="75".parse().expect("不是数字");
          print!("{}",guess);
      }
      
      在这里插入图片描述

标量类型

  • 一个标量类型代表一个单个的值
  • Rust 有四个主要的标量类型
    • 整数类型
    • 浮点类型
    • 布尔类型
    • 字符类型

整数类型

  • 整数类型没有小数部分 例如 U32 就是一个无符号的整数类型,占据32 位的空间
  • 无符号整数类型以u 开头
  • 有符号整数类型以i开头

在这里插入图片描述 arch是系统架构

isize 和 usize 类型 - isize 和 usize类型的位数由程序运行的计算机的架构所决定:- 如果是 64 位计算机,那就是 64 位的 - 使用 isize或usize 的主要场景是对某种集合进行索引操作。

在这里插入图片描述 在这里插入图片描述

浮点类型

  • Rust 有两种基础的浮点类型,也就是含有小数部分的类型
    • f32,32位,单精度
    • f64,64位,双精度
  • Rust 的浮点类型使用了IEEE-754标准来表述
  • f64 是默认类型,因为在现代 CPU 上f64 和f32的速度差不多,而且精度更高

在这里插入图片描述 数字操作,加减乘除 在这里插入图片描述

布尔类型

  • Rust 的布尔类型也有两个值: true 和 false
  • 一个字节大小
  • 符号是 bool 在这里插入图片描述

字符类型

  • Rust 语言中 char 类型被用来描述语言中最基础的单个字符
  • 字符类型的字面值使用单引号
  • 占用 4 字节大小
  • 是 Unicode 标量值,可以表示比 ASCII 多得多的字符内容:拼音、中日韩文、零长度空白字符、emoji 表情等。
    • U+0000 到U+D7FF
    • U+EO00到U+10FFFF 但 Unicode 中并没有“字符”的概念,所以直觉上认为的字符也许与 Rust 中的概念并不相符 在这里插入图片描述

3.3 复合类型

复合类型可以将多个值放在一个类型里。 Rust 提供了两种基础的复合类型: 元组 (Tuple )、数组

Tuple

  • Tuple 可以将多个类型的多个值放在一个类型里
  • Tuple 的长度是固定的:一旦声明就无法改变

创建 Tuple - 在小括号里,将值用逗号分开 - Tuple 中的每个位置都对应一个类型,Tuple 中各元素的类型不必相同

Rust
fn main() {
    let tup:(i32,f64,u8)=(500,6.4,1);
    println!("{},{},{}",tup.0,tup.1,tup.2);//500,6.4,1

}
获取 Tuple 的元素值 可以使用模式匹配来解构 (destructure)一个 Tuple 来获取元素的值

Rust
fn main() {
    let tup:(i32,f64,u8)=(500,6.4,1);
    let (x,y,z)=tup;
    println!("{},{},{}",x,y,z)
}

在这里插入图片描述

访问 Tuple 的元素 在 tuple 变量使用点标记法,后接元素的索引号

数组

  • 数组也可以将多个值放在一个类型里
  • 数组中每个元素的类型必须相同
  • 数组的长度也是固定的

声明一个数组 在中括号里,各值用逗号分开

Rust
fn main() {
    let arr=[1,2,3];
}
在这里插入图片描述

数组的用处 - 如果想让你的数据存放在 stack( ) 上而不是 heap(堆)上,或者想保证有固定数量的元素,这时使用数组更有好处 数组没有 Vector 灵活(以后再讲) - Vector 和数组类似,它由标准库提供 - Vector 的长度可以改变 如果你不确定应该用数组还是 Vector,那么估计你应该用 Vector。

Rust
fn main() {
    let months=[
        "1月",
        "2月",
    ];
}
数组的类型 - 数组的类型以这种形式表示:[类型;长度] - 例如: let a:[i32; 5] =[,2, 3,4, 5]; 另一种声明数组的方法 如果数组的每个元素值都相同,那么可以在: - 在中括号里指定初始值 - 然后是一个";" - 最后是数组的长度 例如: let a=[3;5];它就相当于: let a =[3,3,3,3,3];

Rust
fn main() {
    let a =[5;7];
}
访问数据的元素 在这里插入图片描述

编译器可以检测出简单逻辑的错误 在这里插入图片描述 下图超出了索引,但是复杂的逻辑让编译器没法检查出来 在这里插入图片描述

3.4 函数

  • 声明函数使用 fn 关键字
  • 依照惯例,针对函数和变量名,Rust 使用 snake case 命名规范

Rust
fn main() {
    println!("main function");
    another_function();
}
fn another_function(){
    println!("another_function!");
}
传入函数的参数必须指明类型

Rust
fn main() {
    println!("main function");
    another_function(7);
}
fn another_function(x:i32){
    println!("another_function!{}",x);
}
函数体中的语句与表达式 - 函数体由一系列语句组成,可选的由一个表达式结束 - Rust 是一个基于表达式的语言 - 语句是执行一些动作的指令 - 表达式会计算产生一个值

Rust
fn main() {
    let x=5;
    let y={
        let x=1;
        x+3
    };
    println!("{}",x);//5
    println!("{}",y);//4
}
  • 函数的定义也是语句
  • 语句不返回值,所以不可以使用 let 将一个语句赋给一个变量(例子) 函数的返回值
  • 在 ->符号后边声明函数返回值的类型,但是不可以为返回值命名
  • 在 Rust 里面,返回值就是函数体里面最后一个表达式的值
  • 若想提前返回,需使用 return 关键字,并指定一个值- 大多数函数都是默认使用最后一个表达式最为返回值

Rust
fn main() {
   let x=five();
   let y:i32=another_five(x);
   print!("x={}; y={}",x,y);//x=5; y=10
}
fn five()->i32{
    5
}
fn another_five(x:i32)->i32{
    x+5
}
在这里插入图片描述注解 在这里插入图片描述 还有文档注解,后续补充

3.5 控制流-if

if 表达式

  • if 表达式允许您根据条件来执行不同的代码分支
    • 这个条件必须是 bool 类型
  • if 表达式中,与条件想关联的代码块就叫做分支 (arm)。
  • 可选的,在后边可以加上一个 else 表达式
Rust
fn main() {
   let number =3;
   if number <5 {
    println!("True");
   }else {
       println!("False")
   }
}
Rust
fn main() {
   let number =3;
   if number%4==0 {
        println!("可以被4整除")
   }else if number%3==0 {
        println!("可以被4整除")
   }else if number%2==0 {
        println!("可以被2整除")
   }else {
        println!("不能被4,3,2整除")
   }   
}

使用 else if 处理多重条件 - 但如果使用了多于一个elseif,那么最好使用match 来重构代码

在let 语句中使用 if - 因为 if 是一个表达式,所以可以将它放在let 语句中等号的右边

注意,分支返回的类型必须一样

Rust
fn main() {
    let condition =true;
    let number =if condition {5} else {6};
    println!("{}",number);
}
在这里插入图片描述

3.6 控制流-循环

Rust 的循环 - Rust 提供了 3 种循环: loop,while 和 for

loop 循环

  • loop 关键字告诉 Rust 反复的执行一块代码,直到你喊停
Rust
fn main() {
    let mut count = 0;
    let result =loop{
        count+=1;
        if count==10 {
            break count*2;
        }
    };
    println!("{}",result);//20
}
  • 可以在loop 循环中使用 break 关键字来告诉程序何时停止循环

while 条件循环

  • 另外一种常见的循环模式是每次执行循环体之前都判断一次条件。
  • while 条件循环为这种模式而生
Rust
fn main() {
    let mut number = 3;
    while number != 0 {
        println!("{}",number);
        number-=1;
    }
    println!("发射");
}

Bash
$ cargo run
   Compiling variable v0.1.0 (D:\Workplace\Rust\study\variable)
    Finished dev [unoptimized + debuginfo] target(s) in 0.23s
     Running `target\debug\variable.exe`
3
2
1
发射
使用 for 循环遍历集合 - 可以使用 while 或 loop 来遍历集合,但是易错且低效 - 使用 for 循环更简洁紧凑,它可以针对集合中的每个元素来执行一些代码 - 由于 for 循环的安全、简洁性,所以它在 Rust 里用的最多

Rust
fn main() {
    let a =[10,20,30,40,50];
    let mut index = 0;
    while index < 5 {
       println!("The value is {}",a[index]);
       index+=1;
    }
}

这样写容易出错 改用for,更安全,不容易超出索引

Rust
fn main() {
    let a =[10,20,30,40,50];
    for element in a.iter(){
        println!("The value is: {}",element);
    }
}
Range - 标准库提供 - 指定一个开始数字和一个结束数字,Range 可以生成它们之间的数字(不含结束) - rev 方法可以反转 Range

Rust
fn main() {

    for number in(1..4).rev(){
        println!("{}",number);
    }
}

Reference

[1] 软件工艺师. (2020年10月21日). Rust编程语言入门教程(Rust语言/Rust权威指南配套)【已完结】[视频]. Bilibili. https://www.bilibili.com/video/BV1hp4y1k7SV.