Rust 类型学习笔记

笔记要点:

  • 掌握 Rust 语言基本的类型

Rust 语言基础数据类型(Primitive Types)主要有如下几类:

标量

整数

Length Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

浮点数:f32, f64

整数使用示例:

fn main() {
    let a: i32 = 10;
    let f = 3.14;
    let b = 5i8;
    let c = 100_000_000;
    let pi = 3.141_592_653_59f64;

    let y = a + f;
    println!("{}", y);
}

从以上例子可以总结如下几个要点:

  • 可以在字面值后面接类型:let b = 5i8;
  • 可以用下划线分割数字以提高可读性。
  • 可以不用写类型提示,而使用类型推导。

但运行以上代码会报错:

error[E0277]: cannot add `{float}` to `i32`
  --> src\main.rs:29:15
   |
29 |     let y = a + f;
   |               ^ no implementation for `i32 + {float}`
   |
   = help: the trait `Add<{float}>` is not implemented for `i32`

error:

这是因为 Rust 语言不支持数字类型的隐式转换,使用 as 进行类型转换:

fn main() {
    let a: i32 = 10;
    let f = 3.14;

    let y = a as f32 + f;
    println!("{}", y); // 13.14
}

字符:char

fn main() {
    let x = 'x';
    let y: char = '😎';
    let zh = '中';
    println!("{}: {}", x, x.len_utf8());
    println!("{}: {}", y, y.len_utf8());
    println!("{}: {}", zh, zh.len_utf8())
}

运行代码:

x: 1
😎: 4
中: 3

以上示例表明 Rust 的字符是 Unicode 字符。注意字符要用单引号。

布尔型:bool

布尔类型只有两个值:true, false,不能把 0, None 等用作布尔型。

let is_active = true;
let has_finished: bool = false;

单位类型(unit type): ()

常用作表达式的默认返回值,一个表达式没有显示返回值,就默认返回 ()

fn plus_one(x: &i32) {
    let y = x + 1;
    // do something else
}

fn main() {
    let x = 42i32;
    println!("{:?}", plus_one(&x)); // ()
}

组合类型

数组(array):[T; length]

use std::mem;

fn main() {
    let numbers: [i32; 5] = [1, 2, 3, 4, 5];
    println!("index 0: {}", numbers[0]);
    println!("out of index: {}", numbers[5]);

    println!("length: {}", numbers.len());

    println!(
        "i32: {} bytes, numbers array: {} bytes",
        mem::size_of_val(&0i32),
        mem::size_of_val(&numbers)
    );
    let zeros = [0i32; 5];
    println!("{:?}", zeros);
}

运行以上代码会报错:

error: this operation will panic at runtime
 --> src\main.rs:6:34
  |
6 |     println!("out of index: {}", numbers[5]);
  |                                  ^^^^^^^^^^ index out of bounds: the length is 5 but the index is 5
  |
  = note: `#[deny(unconditional_panic)]` on by default

error: 

这是因为我们故意卖了一个破绽:println!("out of index: {}", numbers[5]);,注释本行后运行:

index 0: 1
length: 5
i32: 4 bytes, numbers array: 20 bytes
[0, 0, 0, 0, 0]

📝要点总结:

(1)数组声明需要指定元素类型与元素个数,元素个数在编译时就确定了,运行时不能改变。数组在内存上表现为一段连续的内存,访问数组元素的时间复杂度为常数。

(2)类型提示可以使用类型推导而省略。

(3)Rust 语言不允许数组越界。

接下来我们学习一种与数组紧密相关的类型:

切片(slice)

先看一个例子:

fn main() {
    let numbers: [i32; 5] = [1, 2, 3, 4, 5];
    let skip_first = &numbers[1..];
    println!("&numbers[1..]: {:?}", skip_first);

    println!("&numbers[1..3]: {:?}", &numbers[1..3]);
    println!("&numbers[..3]: {:?}", &numbers[..3]);
    println!("&numbers[..]: {:?}", &numbers[..]);
}

运行可以得到:

&numbers[1..]: [2, 3, 4, 5]
&numbers[1..3]: [2, 3]
&numbers[..3]: [1, 2, 3]
&numbers[..]: [1, 2, 3, 4, 5]

📝 要点总结:

(1)切片就是数组的一部分,相当于一个子数组。

(2)切片的规则遵循左闭右开惯例:$[start, end)$。

(3)利用切片可以非常方便的截取数组。

字符串切片(&str)

字符串是 Rust 语言的一个难点。事实上,字符串类型是每种语言用得最多的类型,因为计算机本质上是为人服务的。某些底层语言例如 C 语言处理字符串是相当痛苦的,因为它把字符串当做数组处理,由此带来许多问题:数组大小不可变,数组越界等,而且字符串相关的函数用起来也不是很方便。许多高级语言对字符串进行了封装,其中接口清晰、好用的是 JavaScript String,建议通过这个学习字符串。

Rust 语言常用的字符串分为两种:一种是字符串切片(&str),另一种是堆字符串(String),先看例子:

fn main() {
    // example 1
    let hello = "你好";
    let rust: &'static str = "Rust";
    println!("{}, {}", hello, rust); // 你好, Rust

    // example 2
    println!("{}", hello.len()); // 6
    println!("{}", hello.chars().count()); // 2
    println!("{}", hello.is_empty()); // false

    // example 3
    // println!("{}", hello[0]);
    println!("{:?}", &hello[..3]); // "你"

    // example 4
    let path = "/home/root/rust \n";
    println!("{:?}", path.starts_with("/")); // true
    println!("{:?}", path.trim()); // "/home/root/rust"
}

📝 要点总结:

(1)例1表明 Rust 字符串字面值是 &'static str'static 表示该字符串具有静态生命周期。

(2)例2表明 Rust 字符串长度与字符个数是两码事。

(3)例3展示了切片的用法,可以尝试切一下 &hello[..2],看看会发生什么。

(4)例4展示了一些有关字符串处理的方法,更多方法请阅读 str

补充:堆字符串用法:

fn main() {
    let mut hello = String::from("你好");
    hello.push_str(", rust");
    println!("{:?}", hello); // "你好, rust"

    println!("{:?}", hello + ", 嗯mua"); // "你好, rust, 嗯mua"

    let mut result = String::new();
    result = format!("{} + {} = {}", 600, 66, 600 + 66);
    println!("{:?}", result); // "600 + 66 = 666"
}

如果你不知道用哪个,可以使用如下策略:

(1)常字符串:不变的,能放进源代码里的就用 &str

(2)堆字符串:可变的,从网络,文件系统、用户输入获取的字符串就用堆字符串。

💡 约定:以后我们把堆字符串简称为字符串,常字符串,静态字符串,字符串切片都表示 &str

元组(tuple):(T1, T2, ...)

fn main() {
    let a: (i32, f64, bool, char) = (1, 1.5, true, 'a');
    println!("{:?}", a);

    let complex = (0, 1);
    println!("real:{}, image: {}", complex.0, complex.1);

    let (real, image) = complex;
    println!("complex= {} + {}i", real, image);

    let matrix = ((1.0, 2.0, 3.0), (4.0, 5.0, 6.0));
    println!("{:?}", matrix);

    let (first_row, _) = matrix;
    println!("first row: {:?}", first_row);
}

运行上述程序:

(1, 1.5, true, 'a')
real:0, image: 1
complex= 0 + 1i
((1.0, 2.0, 3.0), (4.0, 5.0, 6.0))
first row: (1.0, 2.0, 3.0)

📝 要点总结:

(1)可以看到,元组是由() 组织起来的一个整体,它的元素类型可以不同。

(2)第二个例子我们用元组表示一个虚数:把第一个元素当做实部,把第二个元素当做虚部。可以看到元组获取元素的方法是 complex.0

(3)可以用类似 let (real, image) = complex; 的方法解构(Destruct)元组并把相应的值绑定到某些变量,不用的值可以用 _ 跳过。

更多资料

  1. 官网文档:doc.rust-lang.org/std/index.html#p...
  2. 学习博客:learning-rust.github.io/docs/a8.pr...
  3. 官方示例:doc.rust-lang.org/rust-by-example/...
  4. 官方教程:doc.rust-lang.org/book/ch03-02-dat...
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!