生信小白学Rust-02

发布于:2025-04-17 ⋅ 阅读:(28) ⋅ 点赞:(0)

基本类型

Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:

  • 数值类型:有符号整数 (i8, i16, i32, i64, isize)、 无符号整数 (u8, u16, u32, u64, usize) 、浮点数 (f32, f64)、以及有理数、复数

有符号整数:二进制补码表示,包含正负数;每个有符号类型规定的数字范围是 -(2^n - 1) ~ 2^n - 1 - 1;i8范围为-128~127;isize(指针大小,32位系统=i32,64位系统=i64)

无符号整数:仅表示非负数;u8范围为0~255

浮点数:小心浮点数陷阱

  • 字符串:字符串字面量和字符串切片 &str
  • 布尔类型:truefalse
  • 字符类型:表示单个 Unicode 字符,存储为 4 个字节;Rust 的字符只能用 '' 来表示, "" 是留给字符串的。

Rust 的字符不仅仅是 ASCII,所有的 Unicode 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode 值的范围从 U+0000 ~ U+D7FFU+E000 ~ U+10FFFF

  • 单元类型:即 () ,其唯一的值也是 ()

数字运算

fn main() {
    // 编译器会进行自动推导,给予twenty i32的类型
    let twenty = 20;
    // 类型标注
    let twenty_one: i32 = 21;
    // 通过类型后缀的方式进行类型标注:22是i32类型
    let twenty_two = 22i32;

    // 只有同样类型,才能运算
    let addition = twenty + twenty_one + twenty_two;
    println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);

    // 对于较长的数字,可以用_进行分割,提升可读性
    let one_million: i64 = 1_000_000;
    println!("{}", one_million.pow(2));

    // 定义一个f32数组,其中42.0会自动被推导为f32类型
    let forty_twos = [
        42.0,
        42f32,
        42.0_f32,
    ];

    // 打印数组中第一个值,并控制小数位为2位
    println!("{:.2}", forty_twos[0]);
}

序列

Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:

for i in 1..=5 {
    println!("{}",i);
}

常量(constant)

  • 常量不允许使用 mut。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。
  • 常量使用 const 关键字而不是 let 关键字来声明,并且值的类型必须标注。
// Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,
// 另外对数字字面量可插入下划线以提高可读性
const MAX_POINTS: u32 = 100_000;

变量可变

还是第一次听这种说法🤭

Rust 的变量在默认情况下是不可变的。前文提到,这是 Rust 团队为我们精心设计的语言特性之一,让我们编写的代码更安全,性能也更好。当然你可以通过 mut 关键字让变量变为可变的,让设计更灵活。

fn main() {
    let x = 5;
    println!("the value of x is :{}", x);
    let x = 4;
    println!("the value of x is :{}", x);
    x = 6;
    println!("the value of x is :{}", x);
}

这样会得到报错信息:

(base) PS D:\000zyf\Learning\rust_learn\variables> cargo run
   Compiling variables v0.1.0 (D:\000zyf\Learning\rust_learn\variables)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src\main.rs:6:5
  |
4 |     let x = 4;
  |         - first assignment to `x`
5 |     println!("the value of x is :{}", x);
6 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
4 |     let mut x = 4;
  |         +++

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` (bin "variables") due to 1 previous error

具体的错误原因是 cannot assign twice to immutable variable x(无法对不可变的变量进行重复赋值),因为我们想为不可变的 x 变量再次赋值。

这种错误是为了避免无法预期的错误发生在我们的变量上:一个变量往往被多处代码所使用,其中一部分代码假定该变量的值永远不会改变,而另外一部分代码却无情的改变了这个值,在实际开发过程中,这个错误是很难被发现的,特别是在多线程编程中。

不过要想让某一变量可以被改变也很简单,只需要用mut就好,像下面这样:

fn main() {
    let x = 5;
    println!("the value of x is :{}", x);
    let mut x = 4;
    println!("the value of x is :{}", x);
    x = 6;
    println!("the value of x is :{}", x);
}

// (base) PS D:\000zyf\Learning\rust_learn\variables> cargo run
//    Compiling variables v0.1.0 (D:\000zyf\Learning\rust_learn\variables)
//     Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.04s
//      Running `target\debug\variables.exe`
// the value of x is :5
// the value of x is :4
// the value of x is :6

补充

在编写代码进行学习的时候,无意中将第二个变量前面也加了let,按理来第一次没加mut这个代码应该报错的,但却成功运行了,又稍微了解了一下:

Rust变量遮蔽与可变性解析

工作正常的代码分析

fn main() {
    let x = 5;                          // x 是不可变的,值为5
    println!("the value of x is :{}", x);
    let mut x = 4;                      // 创建一个新的可变变量x,遮蔽(shadow)了原来的x
    println!("the value of x is :{}", x);
    x = 6;                              // 可以修改这个可变的x
    println!("the value of x is :{}", x);
}

这段代码能够正常工作是因为:

第一个 x 是不可变的,值为5

使用 let mut x = 4 声明了一个全新的变量 x,它遮蔽了之前的 x

这个新的 x 是可变的(mut),所以后续的 x = 6 赋值操作是合法的

无法工作的代码分析

fn main() {
    let mut x = 5;                     // x 是可变的,值为5
    println!("the value of x is :{}", x);
    let x = 4;                         // 创建一个新的不可变变量x,遮蔽了原来的x
    println!("the value of x is :{}", x);
    x = 6;                             // 错误!新的x是不可变的,不能被修改
    println!("the value of x is :{}", x);
}

这段代码无法工作的原因是:

第一个 x 虽然是可变的,但它随后被遮蔽了

第二个 let x = 4 创建了一个新的变量,默认是不可变的(没有使用mut关键字)

当尝试 x = 6 时,编译器会报错,因为当前作用域中的 x 是不可变的

核心概念

变量遮蔽:在Rust中,使用let关键字重新声明同名变量会创建一个全新的变量,完全遮蔽之前的变量

变量可变性:每个新变量的可变性由其声明时是否使用mut关键字决定,与被遮蔽的同名变量无关

要修复第二个例子,可以将第二个声明改为 let mut x = 4;,这样后续的赋值操作就合法了。

变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。


选择可变还是不可变,更多的还是取决于你的使用场景,例如不可变可以带来安全性,但是丧失了灵活性和性能(如果你要改变,就要重新创建一个新的变量,这里涉及到内存对象的再分配)。而可变变量最大的好处就是使用上的灵活性和性能上的提升。

例如,在使用大型数据结构或者热点代码路径(被大量频繁调用)的情形下,在同一内存位置更新实例可能比复制并返回新分配的实例要更快。使用较小的数据结构时,通常创建新的实例并以更具函数式的风格来编写程序,可能会更容易理解,所以值得以较低的性能开销来确保代码清

忽略未使用变量

告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头

fn main() {
    let _x = 5;
    let y = 10;
}

// warning: unused variable: `y`
//  --> src/main.rs:3:9
//   |
// 3 |     let y = 10;
//   |         ^ help: 如果 y 故意不被使用,请添加一个下划线前缀: `_y`
//   |
//   = note: `#[warn(unused_variables)]` on by default

变量解构

let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:

fn main() {
    let (a, mut b): (bool,bool) = (true, false);
    // a = true,不可变; b = false,可变
    println!("a = {:?}, b = {:?}", a, b);

    b = true;
    assert_eq!(a, b);
}
// a = true, b = false

解构式赋值

struct Struct {
    e: i32
}

fn main() {
    let (a, b, c, d, e);

    (a, b) = (1, 2);
    // _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _
    [c, .., d, _] = [1, 2, 3, 4, 5];
    Struct { e,.. } = Struct { e: 5 };

    assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
    println!("{}",a)
}

网站公告

今日签到

点亮在社区的每一天
去签到