Rust的基础数据类型、变量系统、类型转换以及实战应用

好的,我们来系统地梳理一下 Rust 的这些核心基础概念。这对于初学者建立坚实的知识框架至关重要。

图片[1]_Rust的基础数据类型、变量系统、类型转换以及实战应用_知途无界

1. Rust 基础数据类型

Rust 是静态强类型语言,所有变量的类型必须在编译期确定。Rust 的数据类型主要分为两大类:​标量类型​ 和 ​复合类型

A. 标量类型

代表一个单独的值。

  1. 整数
    • 有符号整数(正或负):i8, i16, i32, i64, i128, isize(指针宽度)
    • 无符号整数(只能是正或零):u8, u16, u32, u64, u128, usize(指针宽度)
    • isizeusize 的长度取决于程序运行的计算机架构(32位或64位)。
    • 整数字面量可以用 _ 作为分隔符,例如 1_000_000
    • 默认类型是 i32
  2. 浮点数
    • f32:32位,单精度浮点数。
    • f64:64位,双精度浮点数。​默认类型
  3. 布尔型
    • bool:只有两个值,truefalse
  4. 字符
    • char:用单引号 ' 定义,代表一个 Unicode 标量值(如 'a', '中', '🦀')。它占4个字节。

B. 复合类型

将多个值组合成一个类型。

  1. 元组
    • 将不同类型的值组合在一起。
    • 定义:let tup: (i32, f64, u8) = (500, 6.4, 1);
    • 访问元素:
      • 解构:let (x, y, z) = tup;
      • 使用 . 加索引:let five_hundred = tup.0;
  2. 数组
    • 相同类型的值存储在连续的内存块中。
    • 定义:let arr: [i32; 5] = [1, 2, 3, 4, 5];
    • 初始化重复值:let zeros = [0; 10]; // 长度为10,全是0的数组。
    • 访问元素:arr[0]。​越界访问会在运行时 panic!​​(这是 Rust 内存安全的重要体现)。

2. Rust 变量系统

Rust 的变量系统围绕着 ​所有权​ 展开,但其基础是可变性。

A. 声明与可变性

  • 不可变变量​:默认情况下,变量是不可变的。 let x = 5; // x = 6; // 错误:不能给不可变变量赋值
  • 可变变量​:使用 mut 关键字使其可变。 let mut y = 5; y = 6; // 正确 println!("The value of y is: {}", y); // 输出:6

B. 变量遮蔽

可以使用相同的变量名来“遮蔽”之前的变量,甚至可以改变其类型。

let z = 5;
let z = z + 1; // 遮蔽前一个z,现在z是6
{
    let z = z * 2; // 在这个作用域内,z被遮蔽为12
    println!("The value of z in the inner scope is: {}", z);
}
println!("The value of z is: {}", z); // 外部的z仍然是6

C. 常量

使用 const 关键字,并且必须注明值的类型

const MAX_POINTS: u32 = 100_000;

常量在整个程序生命周期内有效,通常用于全局配置。


3. Rust 类型转换

Rust 不像其他语言(如 C/C++)那样支持隐式类型转换(“类型提升”)。必须使用显式转换

A. 使用 as 关键字

这是最简单直接的转换方式,主要用于数值类型之间的转换。

let a = 10_i32;
let b = a as u32; // i32 -> u32

let c = 100_u8;
let d = c as i16; // u8 -> i16

let e = 3.14_f32;
let f = e as i32; // f32 -> i32,小数部分被截断 -> 3

注意​:as 转换在某些边界情况下可能不会报错(例如将一个大的 u64 转换为 i8),而是产生溢出结果,使用时需小心。

B. 使用 FromInto Traits

这是更推荐的方式,因为它更安全、更灵活。FromInto 是标准库中定义的 trait。

  • From:定义在目标类型上。如果你为自己的类型实现了 From<T>,那么就可以轻松地将 T 转换为你的类型。
  • Into:定义在源类型上。调用者可以调用 .into() 来转换类型。通常,如果你实现了 From,Rust 会自动为你提供 Into 的实现。
use std::convert::From;

#[derive(Debug)]
struct Number {
    value: i32,
}

// 为 Number 实现 From<i32>
impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

fn main() {
    let num = Number::from(30);
    println!("num is {:?}", num);

    let int = 5;
    // 因为实现了 From<i32>,所以可以自动获得 Into<Number>
    let num2: Number = int.into();
    println!("num2 is {:?}", num2);
}

C. 使用 TryFromTryInto Traits

当转换可能失败时(例如,字符串转数字),应该使用 TryFrom/TryInto,它们返回一个 Result

use std::convert::TryFrom;

fn main() {
    let s = "100";
    let num: i32 = match i32::try_from(s) {
        Ok(n) => n,
        Err(e) => {
            eprintln!("Conversion failed: {}", e);
            return;
        }
    };
    println!("The number is: {}", num);

    let invalid_s = "hello";
    let result = i32::try_from(invalid_s);
    assert!(result.is_err());
}

4. 实战应用:一个简单的命令行计算器

让我们结合以上知识,编写一个简单的计算器,它可以处理两个整数的加法和减法。

use std::io;

// 定义一个枚举来表示支持的运算
enum Operation {
    Add,
    Subtract,
}

fn main() {
    println!("Simple Calculator");
    println!("Enter an operation (add or subtract):");

    let mut operation_input = String::new();
    io::stdin()
        .read_line(&mut operation_input)
        .expect("Failed to read line");

    let operation = if operation_input.trim() == "add" {
        Operation::Add
    } else if operation_input.trim() == "subtract" {
        Operation::Subtract
    } else {
        println!("Unsupported operation.");
        return;
    };

    println!("Enter first number:");
    let num1 = get_number_from_input();

    println!("Enter second number:");
    let num2 = get_number_from_input();

    // 使用 match 根据枚举进行分支处理
    let result = match operation {
        Operation::Add => add(num1, num2),
        Operation::Subtract => subtract(num1, num2),
    };

    println!("Result: {}", result);
}

// 从用户输入中获取数字,演示类型转换 (String -> i32)
fn get_number_from_input() -> i32 {
    loop {
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("Failed to read line");

        // 使用 try_into 进行可能失败的类型转换
        match input.trim().parse::<i32>() {
            Ok(num) => return num,
            Err(_) => {
                println!("Please enter a valid integer!");
                continue;
            }
        };
    }
}

// 函数演示了基本的运算和类型使用
fn add(a: i32, b: i32) -> i32 {
    a + b // Rust 不需要 return 关键字,最后一个表达式就是返回值
}

fn subtract(a: i32, b: i32) -> i32 {
    let diff = a - b;
    diff // 也可以显式地写成一行,加上分号就变成语句,需要 return
}

代码解析:

  1. 数据类型​:我们使用了 i32 作为数字的默认类型,使用了 String 来处理用户输入。
  2. 变量系统​:operation_input, input, num1, num2 都是可变变量 (mut),因为我们后续需要修改它们的内容(通过 read_line)。
  3. 类型转换​:
    • get_number_from_input 函数中,input.trim().parse::<i32>() 是关键。它将字符串切片 (&str) ​解析​ 为 i32parse 方法返回一个 Result,我们使用 match 优雅地处理了成功和失败的情况,这是 Rust 惯用的错误处理方式。
    • Operation 枚举本身也是一种类型安全和表达力强的构造,避免了使用魔法数字或字符串来表示操作。
  4. 实战要点​:
    • 所有权​:String::new() 创建的字符串拥有其数据,read_line 获取用户输入后,数据就转移到了这个字符串中。
    • 错误处理​:使用 expect 处理必然成功的操作(如 read_line 返回的 Result 我们这里假设不会出错),使用 match 处理可能失败的操作(如 parse)。
    • 模式匹配​:match 是 Rust 中极其强大的工具,我们用它来处理枚举、处理 ResultOption

通过这个小例子,你可以看到 Rust 的基础概念是如何协同工作,构建出安全、清晰且健壮的应用程序的。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞73 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容