《Rust 编程第一课》 学习笔记 Day 16

108 阅读2分钟

大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。第十六天还是继续和大家一起学习 Rust😊

所有权

值与堆栈的关系

动态数组因为大小在编译期无法确定,所以放在堆上,并且在栈上有一个包含了长度和容量的胖指针指向堆上的内存

每次把变量作为参数传递一次,堆上的内存就会多一次引用。针对堆内存多次引用的问题,不同的编程语言有不同解决方案:

  1. C/C++ 要求开发者手动处理

  2. Java 等语言使用追踪式 GC,定期扫描堆上数据是否还有人引用,来替开发者管理堆内存

  3. ObjC/Swift 使用自动引用计数(ARC),在编译时自动添加维护引用计数的代码,减轻开发者维护堆内存的负担

  4. Rust 觉得一个值最好只有一个拥有者,保证单一所有权。所以定了以下规则:

    • 一个值只能被一个变量所拥有,这个变量被称为所有者
    • 一个值同一个时刻只能有一个所有者(所有权转移 Move 语义),多次赋值只有最后一个才拥有所有权
    • 当所有者离开作用域 (scope),其拥有的值会被丢弃

如果遇到编译器 Error,可以使用 rustc —explain E0382 来查看更详细的信息,E0382 是错误码

Copy 语义

实现 Copy 语义的数据结构:

  1. 原生类型,包括函数、不可变引用和裸指针实现了 Copy;
  2. 数组和元祖,如果其内部的数据结构实现了 Copy,那它们都实现了 Copy;
  3. 可变引用没有实现 Copy;
  4. 非固定大小的数据结构,没有实现 Copy;

标准库实现了 Copy trait 的所有数据结构

Rust 通过单一所有权来限制任意引用的行为

猜猜看游戏


use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("Guess the numner!");

		// start..=end 是一个范围表达式,意思是包含上下端点,例如 1..=100 是指 1 和 100 之间的数字
    let secret_number = rand::thread_rng().gen_range(1..=100);

    loop {
        println!("Please input your guess.");

				// String::new() 这里的 new 是 String 类型的一个关联函数,关联函数是针对类型实现的
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess) // & 表示这个参数是一个引用,允许多处代码访问同一处数据,而无需在内存里多次拷贝
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
        println!("Your guess is: {guess}");

				// 一个 match 表达式由分支构成,一个分支包含一个模式和表达式开头的值与分支模式相匹配时应该执行的代码
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}