变量与数据

学习目标

学完本章后,你将能够:

  • 理解和区分 Rust 中所有的基本数据类型
  • 使用整数、浮点数、布尔值和字符
  • 创建和使用元组(tuple)与数组(array)
  • 区分可变与不可变变量
  • 知道什么时候该用什么类型

一、从最简单的变量开始

还记得上一章的 let x = 5; 吗?这就是声明变量的最简单方式:

fn main() {
    let x = 5;
    println!("x = {}", x);
}

输出:

x = 5

let x = 5; 做了三件事:

  1. 在内存中开辟了一小块空间
  2. 把数字 5 存进去
  3. 给这块空间贴上标签 x

之后提到 x,计算机就知道你要找那个存了 5 的空间。


二、不可变:Rust 的保护机制

2.1 变量默认不可变

fn main() {
    let x = 5;
    x = 6;  // 编译错误!
}

Rust 的规矩是:你声明一个变量之后,它的值就不能随便改。

为什么?想象你在做数学题:

第一步:设 x = 5
第二步:计算 x + 3  ← 这当然等于 8
第三步:设 y = x    ← y 肯定是 5

如果第一步后 x 偷偷变成了 6,结果就全乱了。Rust 帮你把这个”偷偷变”的可能消除了。

2.2 mut:我能改变它吗?可以!

如果你真的需要改变一个变量,用 mut

fn main() {
    let mut x = 5;
    x = 6;
    println!("x = {}", x);  // x = 6
}
flowchart LR
    A1["5"] -->|mut| B1["6 ✓ 可以改"]
    A2["5"] -->|无 mut| B2["6 ✗ 不能改"]

经验法则:能不动就不动。如果发现确实需要改变,再加 mut


三、整数的世界

3.1 最简单的整数

fn main() {
    let age = 18;
    let year = 2026;
    let count = -5;    // 负数也可以
    println!("年龄:{},年份:{},计数:{}", age, year, count);
}

在 Rust 里,如果不标注类型,整数默认是 i32

3.2 整数家族

Rust 提供了很多种整数类型,区别在于能存多大的数字能不能存负数

类型含义范围(约)比喻
i88位有符号-128 ~ 127一个车牌号
i1616位有符号-32768 ~ 32767一座城市的人口
i3232位有符号-21亿 ~ 21亿中国的人口
i6464位有符号超大银河系的星星数
i128128位有符号超级大用不完
类型含义范围比喻
u88位无符号0 ~ 255一天的小时数
u1616位无符号0 ~ 65535端口号
u3232位无符号0 ~ 42亿IP地址
u6464位无符号超大硬盘容量
u128128位无符号超级大
usize指针大小看系统数组索引

命名规则

  • i 开头:有符号(能表示正和负,就像 i32
  • u 开头:无符号(只有正数,就像 u32
  • 数字代表占用多少位

大多数人 99% 的时间只用一个类型:i32

fn main() {
    let a: i8 = 100;
    let b: u8 = 200;
    let c: i64 = 9000000000;
    println!("a={}, b={}, c={}", a, b, c);
}

3.3 选哪种整数?

场景推荐类型
普通整数i32
年龄、计数i32u32
数组索引usize
特殊小范围u8(如颜色值 0-255)
超大数字i64u64

四、小数:浮点数

fn main() {
    let pi = 3.14159;
    let e = 2.71828;
    let half = 0.5;
    println!("π={}, e={}, 一半={}", pi, e, half);
}

浮点数有两种:

类型名称精度用途
f32单精度约7位小数一般计算
f64双精度约15位小数科学计算(默认)

默认是 f64

fn main() {
    let a: f32 = 3.14;   // 明确标注 f32
    let b = 3.14;        // 默认 f64
    let c = 2.5_f32;     // 后缀写法:也是 f32
    println!("a={}, b={}, c={}", a, b, c);
}

注意:浮点数直接比较时可能有精度问题,不过现在先不管,后面会讲到。


五、布尔:真还是假?

fn main() {
    let is_raining = true;
    let is_sunny = false;
 
    println!("下雨了吗?{}", is_raining);
    println!("天晴了吗?{}", is_sunny);
}

输出:

下雨了吗?true
天晴了吗?false

布尔类型只有两个值:true(真)和 false(假)。就像电灯开关 — 要么开,要么关。

布尔在条件判断中大量使用,下一章就会见到更多。

fn main() {
    let has_ticket: bool = true;   // 有门票
    let is_vip: bool = false;      // 不是VIP
    println!("有票:{},VIP:{}", has_ticket, is_vip);
}

六、字符:不仅仅是一个字母

fn main() {
    let letter = 'A';
    let digit = '7';
    let emoji = '😊';
    let chinese = '好';
 
    println!("字母:{},数字:{},表情:{},中文:{}", letter, digit, emoji, chinese);
}

Rust 的 char 类型很强大 — 它能存任何 Unicode 字符!英文、中文、日文、表情符号,都没问题。

注意区分:

  • 'A' — 单引号 = 单个字符(char)
  • "A" — 双引号 = 字符串(str)

七、复合类型:把数据打包

7.1 元组(Tuple)—— 固定大小的”打包袋”

fn main() {
    let t = (1, "hello", 3.14);
    println!("整数:{},文字:{},小数:{}", t.0, t.1, t.2);
}

元组像是一个小袋子,可以把不同类型的数据装在一起:

  • t.0 — 访问第一个元素
  • t.1 — 访问第二个元素
  • t.2 — 访问第三个元素
fn main() {
    // 解构:一次取出所有元素
    let (x, y, z) = (10, 20, 30);
    println!("x={}, y={}, z={}", x, y, z);
 
    // 带有类型的元组
    let person: (&str, i32, bool) = ("小明", 18, true);
    println!("名字:{},年龄:{},在线:{}", person.0, person.1, person.2);
}

元组的特点:

  • 大小固定,创建后不能增减元素
  • 可以装不同类型
  • .数字 访问

7.2 数组(Array)—— 排成一排的相同类型

fn main() {
    // 最简单的数组
    let arr = [1, 2, 3, 4, 5];
    println!("第一个:{},第三个:{}", arr[0], arr[2]);
}

输出:

第一个:1,第三个:3

注意:数组索引从 0 开始!

graph LR
    subgraph 数组
        I0["[0]: 1"] --- I1["[1]: 2"] --- I2["[2]: 3"] --- I3["[3]: 4"] --- I4["[4]: 5"]
    end
    I0 -.- L0["arr[0]"]
    I3 -.- L3["arr[3]"]

数组的特点:

  • 所有元素必须是相同类型
  • 大小固定,创建后不能变
  • 用方括号 [ ] 定义
  • [索引] 访问
fn main() {
    // 声明类型和大小
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
 
    // 另一种写法:创建 100 个 0
    let zeros = [0; 100];
 
    println!("第50个零:{}", zeros[49]);  // 索引从0开始,所以49就是第50个
    println!("数组长度:{}", arr.len());
}

7.3 元组 vs 数组

元组 (Tuple)数组 (Array)
写法(1, "a", 3.14)[1, 2, 3, 4, 5]
访问.0, .1, .2[0], [1], [2]
元素类型可以不同必须相同
大小固定固定
长度类型的一部分类型的一部分

八、类型转换

有时候需要把一种类型变成另一种:

fn main() {
    let a: i32 = 42;
    let b: i64 = a as i64;    // i32 转 i64(安全的,变大)
    let c: i8 = a as i8;      // i32 转 i8(注意:如果超出范围会截断)
 
    println!("a={}, b={}, c={}", a, b, c);
 
    let f: f64 = 3.14;
    let i: i32 = f as i32;    // 小数转整数,丢掉小数部分
    println!("f={}, i={}", f, i);  // i = 3
}

九、常量:永远不变的值

fn main() {
    const MAX_PLAYERS: u32 = 100;
    const PI: f64 = 3.1415926535;
    const GREETING: &str = "你好";
 
    println!("最多{}个玩家", MAX_PLAYERS);
    println!("圆周率约:{}", PI);
    println!("{}", GREETING);
}

constlet 的区别:

letconst
可变性默认不可变,可加 mut永远不可变
类型标注可省略必须标注
可以在运行时计算必须在编译时确定
命名任意通常全大写

本章小结

这一章我们学习了 Rust 的数据类型:

  • 整数i8i128(有符号),u8u128(无符号),默认 i32
  • 浮点数f32f64(默认),表示小数
  • 布尔bool,只有 truefalse
  • 字符char,能表示任何单个字符
  • 元组(a, b, c),固定大小,类型可不同
  • 数组[a, b, c],固定大小,类型相同
  • 变量默认不可变,加 mut 可变
  • 常量用 const,类型必须标注

掌握了数据,就像工匠知道了有哪些材料。接下来,我们要学习如何”做决策”。

04-做决策:条件与循环


章节考查

总分100分:概念考查40分 + 判断正误20分 + 代码分析15分 + 编程大题15分 + 填空题5分 + 代码补全5分

一、概念考查(每题4分,共40分)

1. Rust 中整数默认是哪种类型?

  • A. i8
  • B. i32
  • C. i64
  • D. u32
点击查看答案

Bi32 是 Rust 默认的整数类型,在绝大多数场景中是性能和范围的平衡选择。

2. 下面哪一个是合法的 char 类型值?

  • A. “A”
  • B. ‘A’
  • C. “好”
  • D. 123
点击查看答案

Bchar 类型用单引号包裹,"A" 是字符串类型。

3. 元组 let t = (1, "hi", 3.14);,访问 3.14 的写法是?

  • A. t[2]
  • B. t.2
  • C. t.3
  • D. t->2
点击查看答案

B。元组用 .索引 访问,索引从 0 开始,所以第三个元素是 t.2

4. let arr = [10; 5]; 创建的数组是什么?

  • A. [10, 5]
  • B. [10, 10, 10, 10, 10]
  • C. [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
  • D. 编译错误
点击查看答案

B[值; 重复次数] 语法表示重复创建,[10; 5] = 5个10。

5. u8 类型的范围是?

  • A. -128 ~ 127
  • B. 0 ~ 255
  • C. -255 ~ 255
  • D. 0 ~ 127
点击查看答案

Bu8 是无符号 8 位整数,只能是 0 到 255(即 2⁸-1)。

6. 要让变量可以改变值,需要加什么关键字?

  • A. var
  • B. mut
  • C. ref
  • D. change
点击查看答案

Bmut 是 mutable 的缩写,let mut x = 5; 表示 x 可被重新赋值。

7. constlet 的一个重要区别是?

  • A. const 可变,let 不可变
  • B. const 必须标注类型,let 可以不标注
  • C. const 可以在运行时赋值
  • D. 没有区别
点击查看答案

B。常量必须显式标注类型,如 const X: i32 = 5;。而 let x = 5; 可以自动推断。

8. 数组和元组的共同点是?

  • A. 元素类型必须相同
  • B. 大小固定
  • C. 元素类型可以不同
  • D. 都使用 [] 访问
点击查看答案

B。数组和元组的大小在创建时就固定了,不能改变。但元素类型规则不同。

9. let x: f64 = 5; 会怎样?

  • A. 正常编译,5 变成 5.0
  • B. 编译错误,类型不匹配
  • C. x 最终是整数 5
  • D. 运行时出错
点击查看答案

A。编译器会进行隐式类型转换(类型可以安全转换时),但这不是好的编码习惯。更好的写法是 5.0

10. 浮点数默认类型是?

  • A. f32
  • B. f64
  • C. f128
  • D. 没有默认类型,必须标注
点击查看答案

B。与整数默认 i32 类似,浮点数默认是 f64(双精度)。

二、判断正误(每题2分,共20分)

1. let x = 5; let x = x + 1; 会编译错误。

点击查看答案

错误。这是变量遮蔽(shadowing),是合法的。第二个 let x 创建了一个新的同名变量。

2. 数组 let a = [1, 2, 3]; 可以通过 a[3] 访问到元素 3。

点击查看答案

错误。索引从 0 开始,a[2] 才能访问到 3。a[3] 越界了。

3. i32 可以存正数和负数。

点击查看答案

正确i 开头表示有符号(signed),可以存负数。

4. Rust 的 char 只能存英文字母。

点击查看答案

错误。Rust 的 char 可以存任何 Unicode 字符,包括中文、日文、表情符号等。

5. let a: u32 = -5; 是合法的。

点击查看答案

错误u32 是无符号类型,不能存负数。

6. let t = (1, 2, 3);t 的类型是元组。

点击查看答案

正确。三个整数也是元组,类型为 (i32, i32, i32)

7. 元组可以通过 t.0 访问元素,也可以通过 t[0] 访问。

点击查看答案

错误。元组只能用 .索引(如 .0)访问,不能用 [索引]

8. let x = 3.14; 中 x 的类型是 f32。

点击查看答案

错误。浮点数默认是 f64

9. let zeros = [0; 1000]; 创建了 1000 个零的数组。

点击查看答案

正确[值; 数量] 是创建重复值的数组的语法。

10. const 可以用在函数内部。

点击查看答案

正确const 可以在任何作用域中使用,包括函数内部。

三、代码分析(每题3分,共15分)

1. 下面代码的输出是什么?

fn main() {
    let t = (10, "hello", 3.14);
    println!("{}", t.1);
}
  • A. 10
  • B. hello
  • C. 3.14
  • D. 编译错误
点击查看答案

B。元组索引从 0 开始,t.1 是第二个元素 "hello"

2. 下面代码的输出是什么?

fn main() {
    let arr = [1, 2, 3, 4, 5];
    println!("{}", arr[1] + arr[3]);
}
  • A. 4
  • B. 6
  • C. 7
  • D. 5
点击查看答案

Barr[1] = 2arr[3] = 42 + 4 = 6

3. 下面代码有什么问题?

fn main() {
    let mut x: i32 = 100;
    let y: i8 = x as i8;
    println!("y = {}", y);
}
  • A. 什么都不输出
  • B. 输出可能不是 100
  • C. 编译错误
  • D. 没有错误
点击查看答案

Bi32i8 可能会丢失数据。如果 x 是 100,i8 能存下(范围 -128~127),所以这次没问题。但如果 x 是 200,转换后结果就不是 200 了。这是潜在的数据截断风险。

4. 下面代码的输出是什么?

fn main() {
    let (a, b) = (5, 10);
    println!("a={}, b={}", a, b);
}
  • A. a=0, b=0
  • B. a=5, b=10
  • C. 编译错误
  • D. a=10, b=5
点击查看答案

B。这是元组解构(destructuring):a 取到 5,b 取到 10。

5. 下面代码有什么错误?

fn main() {
    let x = 3.14;
    let y: i32 = x;
    println!("{}", y);
}
  • A. y 会是 3
  • B. 编译错误,不能把 f64 直接赋给 i32
  • C. 缺少 mut
  • D. 没有错误
点击查看答案

B。Rust 不做隐式类型转换,必须显式用 aslet y: i32 = x as i32;

四、编程大题(15分)

题目: 写一个程序,包含:

  1. 一个不可变的整数变量 a = 10
  2. 一个可变的整数变量 b = 20
  3. b 改成 30
  4. 一个浮点数 pi = 3.14159
  5. 一个布尔 is_done = true
  6. 一个字符 grade = 'A'
  7. 一个元组 info 包含你的名字和年龄
  8. 打印以上所有内容
点击查看答案
fn main() {
    let a = 10;
    let mut b = 20;
    b = 30;
    let pi = 3.14159;
    let is_done = true;
    let grade = 'A';
    let info = ("小明", 18);
 
    println!("a = {}, b = {}", a, b);
    println!("pi = {:.2}", pi);  // .2 表示保留两位小数
    println!("完成了?{}", is_done);
    println!("等级:{}", grade);
    println!("名字:{},年龄:{}", info.0, info.1);
}

评分标准

  • 不可变变量 a(2分)
  • 可变变量 b(3分)
  • 浮点数 pi(2分)
  • 布尔 is_done(2分)
  • 字符 grade(2分)
  • 元组 info(2分)
  • 打印语句(2分)

五、填空题(每题1分,共5分)

1. Rust 中数组的索引从 ______ 开始。

点击查看答案

0。和大多数编程语言一样,Rust 数组索引从 0 开始。

2. ______ 类型的值只能是 truefalse

点击查看答案

bool。布尔类型是 Rust 的逻辑类型。

3. 创建 10 个值为 0 的数组:let a = [______; ______];

点击查看答案

010let a = [0; 10]; 创建 10 个 0。

4. ______ 是无符号整数前缀,如 ______ 范围是 0~255。

点击查看答案

uu8u 表示无符号(unsigned),u8 是 8 位无符号整数。

5. 元组用 ______ 括起来,数组用 ______ 括起来。

点击查看答案

小括号 ()方括号 [](1, 2) 是元组,[1, 2] 是数组。

六、代码补全(共5分)

1. 补全类型标注(2分)

fn main() {
    let count: ______ = 42;
    let price: ______ = 19.99;
    println!("数量:{},价格:{}", count, price);
}
点击查看答案
let count: i32 = 42;
let price: f64 = 19.99;

(整数用 i32 或 u32 均可,浮点数用 f64 或 f32 均可)

2. 补全元组解构(2分)

fn main() {
    let t = ("Rust", 2026);
    let (name, year) = ______;
    println!("{} 发布于 {}", name, year);
}
点击查看答案
let (name, year) = t;

3. 补全数组访问(1分)

fn main() {
    let arr = [100, 200, 300];
    println!("第二个元素:{}", arr[______]);
}
点击查看答案
println!("第二个元素:{}", arr[1]);

(索引从 0 开始,第二个是索引 1)


计分:概念40 + 判断20 + 代码分析15 + 编程15 + 填空5 + 补全5 = 总分100分


练习

练习

题号题目链接知识点
P1001A+B Problemhttps://www.luogu.com.cn/problem/P1001输入输出、变量声明
P1000超级玛丽游戏https://www.luogu.com.cn/problem/P1000顺序结构、println
P1003铺地毯https://www.luogu.com.cn/problem/P1003数组、循环
P1004方格取数https://www.luogu.com.cn/problem/P1004二维数组、mut