03常见编程概念

变量与可变性

Rust 中,变量默认是不可变的(immutable)

  • 不能对不可变(immutable)变量二次赋值;

  • 在变量名之前加 mut 关键字,使其可变;

    1
    2
    let x = 5;  // x 为不可变变量
    let mut x = 5; // x 为可变变量

变量与常量的区别

常量(constants): 常量是不能修改的,

  1. 不允许对常量使用 mut 关键字;

  2. 声明常量使用 const 关键字,不用 let,且必须标明值的类型

  3. 命名方式:

    • 全大写字母,多个字母使用下划线分隔;

    • 值为数字时,可以在数字间加下划线提升可读性;

      1
      const MAX_POINTS: u32 = 100_000;  // 数字中的下划线仅起到增加可读性的作用;
  4. 常量在整个程序生命周期中都有效;

    • 将程序中的硬编码值,声明为常量,有助于代码维护人了解值的意图,同时便于后面修改时仅修改一处值;

隐藏(Shadowing)

定义一个与之前变量同名的变量,当使用该变量时,仅能获取第二个变量的值,即第一个变量被第二个同名变量隐藏了。

  • 可以使用let关键字进行多次隐藏;

使用场景:

  • 变量类型转换的场景;

    1
    2
    let spaces = "   ";  // 先获取输入;
    let spaces = spaces.len(); // 再获取上面变量的长度;

    这样就可以复用同一个变量名,而不是必须创建两个不同变量

数据类型

每个值都属于一个 数据类型(data type)

Rust 是静态类型语言,即在编译时就必须知道所有变量的类型。

两类数据类型:

  • 标量(scalar)类型,代表一个单独的值。有4中基本的标量类型。

    • 整型

      • 没有小数部分的数字

      • 分为,有符号(i,数字可以为负值)和无符号(u,数字不能为负值)

      • 有符号最高位为符号位,可以参考 Java;

      • 有符号数以补码形式存储?

      • 有符号可以存储的数字范围,-(2^n-1) 到 2^(n-1) -1

      • 无符号,0 到 2^n - 1

      • isizeusize 依赖运行程序的计算机架构,64位则是i64/u64,32位的机器则是i32/u32

        主要作为某些集合的索引;

      • 注:除了 byte以外的所有数字字面值,允许使用类型后缀,同时也允许使用_做为分隔符以方便读数;

    • 浮点型

      • 带小数点的数字
      • f32 (单精度浮点数)
      • f64 (默认类型,因为在现在CPU中,它与f32速度几乎一样,不过精度更高)(双精度浮点数)
      • 采用 IEEE-754 标准来表示,
    • 布尔型

      • bool,值:true ,false

        1
        2
        let t = false;  // 自动推断
        let f: bool = true; // 显示指定
      • 主要使用场景是,条件表达式,例如,if 表达式中

    • 字符类型

      • char,单引号,
      • 4 个字节,32bit,代表了一个 Unicode 标量值(范围包含了,U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF 在内的值)
      • 只要是Unicode范围内的值都是有效的char
        • 例如,拼音字母,中文,日文,emoji(绘文字)…
  • 复合(compound),包含多个值的类型

    • 两个原生的复合类型

    • 元组(tuple)

      • 长度固定,一旦声明,其长度不会增加或减少

      • (a,b,c),圆括号定义,多个值的类型可以不同

        1
        let tup: (i32,f64,u8) = (500,6.4,1);
      • 逗号(,)分隔

      • 使用方式:

      • 方式1,模式匹配来解构(destructure)

      • 方式2,使用 . 点符号访问索引

      1
      2
      3
      4
      5
      6
      7
      8
      9
      方式1
      let tup = (500,6.4,1);
      let (x,y,z) = tup; // 通过模式将 tup 元组分成了 x,y,z 三个不同的变量,这里与python有些类似
      println!("The value of y is: {}", y); // 解构后,可以单独使用变量

      方式2
      let x:(i32,f64,u8) = (500,6.4,1);
      let five_hundred = x.0;
      let one = x.2;
    • 数组(array)

      • 与元组不同,数组中每个元素的类型必须相同

      • 固定长度

      • [类型; 长度]方括号定义,逗号分隔(,

        1
        2
        3
        4
        let a = [1,2,3,4]
        let b: [i32; 5] = [1,2,3,4,5]; // 显示标注类型时,需要指明类型和数组长度;
        let c = [3; 5]; // 这样写,表示c有5个元素,都是数字3;
        let c = [3,3,3,3,3];
      • 数据分配在栈(stack)上,一整块在栈上的内存

        当需要的元素固定时,使用数组;

        当不定长时,使用 vector;(标准库提供的与数组类似的,可变的集合类型);

        当不确定应该使用array 还是 vector 时,使用 vector;

      • 访问方式: 使用索引访问,(与Java一样)

        1
        2
        3
        let a = [1,2,3];
        let first = a[0]; // first 的值是1;
        let second = a[1]; // second 的值是2;
      • 数组越界时,Rust会发生 panic,然后立刻推出,不允许继续访问。

使用时,

  • 省略类型(自动推断),即默认 Rust 编译器会自动根据值和其使用方式,推断我们使用的类型。
  • 显示标注类型,当结果可能为多种类型时,即 Rust 编译器无法准确推断值的类型时。

函数如何工作

函数定义的关键字

  • fn
  • 函数和变量名命名风格,snake case风格
    • 所有字母都是小写,并使用**下划线(_)**分隔单词;
  • 函数签名中,必须声明每个参数的类型。多个参数用逗号(,分隔)

函数的返回值

如果有返回值,则需要在箭头(->后声明它的类型 。

重要

  1. 在 Rust 中,函数的返回值等同于函数体最后一个表达式的值

  2. 使用 return 关键字和指定值,可以从函数中提前返回。

  3. 大部分函数隐式的返回最后一个表达式的值。

1
2
3
4
5
6
7
8
fn five() -> i32 {
5
}

fn main() {
let x = five();
println!("The value of x is: {}", x);
}

注:

代码块的值是其最后一个表达式的值,而数字本身就是一个表达式

语句和表达式

语句(statement),不返回值

  • 执行一些操作但不返回值的指令
  • 使用 let 关键字创建变量并绑定一个值是一个语句
  • 函数定义也是一个语句;

表达式(expressions)

  • 计算并产生一个值
  • 表达式的结尾没有分号;如果加上分号就变成语句了,语句不能返回值。
    • 函数体内最后一行作为返回值的是表达式,不能加分号

语句块(block)也是表达式

  • block 是多个语句或表达式的集合;

  • 所以它可以被当做值(value)使用在赋值语句中;

    注意,当做值在赋值语句中使用时,要以;结尾。

  • 如果block中的最后一句是表达式,则当block处在赋值语句中时,最后一个表达式的值将作为返回值并赋值给变量;

  • 如果block中的最一句是语句(以;结尾),则返回值为() ;

注释

在 Rust 中,使用 // 来进行注释

1
// hello,world

控制流

if 表达式

1
2
3
4
5
6
7
if 条件表达式 {

} else if 条件表达式 {

} else {

}

Rust 中 if 的条件表达式直接跟在 if 关键字后面,不需要圆括号()

let 语句中使用 if

因为if 是一个表达式,所以可以将 if 表达式赋值给 let 定义的变量。

1
2
3
4
5
6
let condition = true;
let number = if condition {
5
} else {
6
};

使用循环重复执行

Rust 提供三种循环:

  • [loop](# loop)
  • [while](# while-条件循环)
  • [for](# for-循环)

loop

在 Rust 中,通常使用在希望无限循环的场景中(代替 while ture 的写法)。

for循环一样,使用 break 停止当前循环,使用 continue 跳过本次循环内容,进入下一次循环。

如果存在嵌套loop循环,breakcontinue仅作用于当前层循环。

不同点:

  • 停止多层循环,使用循环标签(loop label),这样break关键字就可以作用在指定的循环层。

    • break/continue 循环标签

    • 循环标签定义: '标签名

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      fn main() {
      let mut count = 0;
      'counting_up: loop { // 外层循环指定一个标签 <'标签名>
      println!("count = {}", count);
      let mut remaining = 10;

      loop {
      println!("remaining = {}", remaining);
      if remaining == 9 {
      break;
      }
      if count == 2 {
      break 'counting_up; // 停止外层循环则
      }
      remaining -= 1;
      }

      count += 1;
      }
      println!("End count = {}", count);
      }
  • 从循环返回值,

    • break 需要返回的内容

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      fn main() {
      let mut counter = 0;

      let result = loop {
      counter += 1;

      if counter == 10 {
      break counter * 2; // 当执行到这里时,会将 counter * 2 的结果20 返回并赋值给 result;
      }
      };

      println!("The result is {}", result);
      }

while 条件循环

与 Java 一样,不同的点在于 条件语句没有圆括号

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut number = 3;

while number != 0 { // 与 if 语句的条件表达式一样
println!("{}!", number);

number = number - 1;
}

println!("LIFTOFF!!!");
}

for 循环

for loops

1
2
3
4
5
for 变量 in 集合 {
//something
}

// 格式与 python 和 shell 的一种有些像

for in 结构体通常使用 Iterator 迭代器来迭代。

有两种方式创建 iterator,

方法一,使用range,即范围表示法a..b 表示范围[a,b),使用a..=b表示范围 [a,b]

for and range

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
// `n` will take the values: 1, 2, ..., 100 in each iteration
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else {
println!("{}", n);
}
}
}

// `n` will take the values: 1, 2, ..., 100 in each iteration
for n in 1..=100 {}

for and iterators

for in 结构体与 Iterator 有几种交互方式

  • into_iter

    • 获取所有权,每次循环都会消费值,即将迭代的值 moved 到循环中。循环结束后该集合就不可重用。
  • iter

    • 借用(不可变借用),循环结束后该集合可重用。
  • iter_mut

    • 可变借用,允许修改集合中的值。
1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let mut names = vec!["Bob", "Frank", "Ferris"];

for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}

println!("names: {:?}", names);
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信