第六章 变量、数据类型与基本运算

引子:计算机程序如何"思考"?

想象一下,你正在教一个完全不懂数学的小朋友做计算。你会怎么做?

你可能会说:"看,这是数字1,这是数字2,如果你把它们放在一起,就变成了3。"

计算机程序也是这样工作的。它就像一个非常听话但有点"笨"的学生,需要你明确地告诉它:

  • 这是什么?(数据类型)
  • 把它放在哪里?(变量)
  • 对它做什么?(运算)

数据的"生命":从输入到输出

让我们看一个简单的例子:计算圆的面积。

人类思维过程:

  1. 我知道圆的面积公式是 π × 半径²
  2. 如果半径是5,那么面积就是 3.14 × 5 × 5 = 78.5

计算机程序思维过程:

  1. 我需要一个地方存储半径值 → 创建变量 radius
  2. 我需要一个地方存储π值 → 创建常量 PI
  3. 我需要一个地方存储计算结果 → 创建变量 area
  4. 执行计算:area = PI * radius * radius
  5. 显示结果
fn main() {
    // 1. 存储数据
    let radius = 5.0;           // 半径
    const PI: f64 = 3.14159;    // 圆周率
    
    // 2. 操纵数据(计算)
    let area = PI * radius * radius;
    
    // 3. 输出结果
    println!("半径为 {} 的圆的面积是:{:.2}", radius, area);
}

程序 = 数据 + 操作

所有的计算机程序,本质上都是在做三件事:

  1. 存储数据 - 把信息放在"盒子"里
  2. 操纵数据 - 对数据进行各种操作(计算、比较、转换等)
  3. 输出结果 - 把处理后的信息展示出来

就像做菜一样:

  • 食材 = 数据(面粉、鸡蛋、糖)
  • 烹饪过程 = 操作(搅拌、加热、冷却)
  • 成品 = 结果(蛋糕)

为什么需要不同的数据类型?

想象你在整理房间:

  • 书籍 → 放在书架上(整整齐齐)
  • 衣服 → 放在衣柜里(叠好分类)
  • 食物 → 放在冰箱里(保持新鲜)
  • 工具 → 放在工具箱里(方便取用)

不同的东西需要不同的存放方式。同样,不同的数据也需要不同的"容器":

  • 数字(年龄、价格)→ 数字类型
  • 文字(姓名、地址)→ 字符串类型
  • 真假(开关状态)→ 布尔类型
  • 多个相关数据 → 数组或元组

从简单到复杂

这一章,我们将从最简单的数据操作开始:

  1. 变量 - 学会创建和使用"数据盒子"
  2. 数据类型 - 了解不同数据的"性格特点"
  3. 基本运算 - 掌握数据之间的"互动方式"

想象一下,你正在写一个简单的计算器程序。这个程序需要:

  • 存储用户输入的数字
  • 进行加减乘除运算
  • 保存计算结果
  • 显示最终答案

这就是我们这一章要学习的内容:如何在Rust中存储和处理数据

让我们从一个简单的例子开始:

fn main() {
    // 存储用户输入的数字
    let number1 = 10;
    let number2 = 5;
    
    // 进行运算
    let sum = number1 + number2;
    let difference = number1 - number2;
    let product = number1 * number2;
    let quotient = number1 / number2;
    
    // 显示结果
    println!("{} + {} = {}", number1, number2, sum);
    println!("{} - {} = {}", number1, number2, difference);
    println!("{} × {} = {}", number1, number2, product);
    println!("{} ÷ {} = {}", number1, number2, quotient);
}

运行这个程序,你会看到:

10 + 5 = 15
10 - 5 = 2
10 × 5 = 50
10 ÷ 5 = 2

在这个例子中:

  • number1number2sumdifferenceproductquotient 都是变量,用来存储数据
  • 105整型数据
  • +-*/运算符
  • println! 用来显示结果

这一章,我们就来详细学习:

  1. 如何声明和使用变量
  2. Rust支持哪些数据类型
  3. 如何进行基本运算
  4. 与C语言相比有什么不同

准备好了吗?让我们开始吧!

6.1 变量声明与可变性

什么是变量?

想象变量就像一个盒子,可以存放东西。在编程中,变量用来存储数据,比如数字、文字等。

在Rust中声明变量

在Rust中,我们用let关键字来创建一个"盒子"(变量):

#![allow(unused)]
fn main() {
let age = 25;        // 创建一个名为age的盒子,里面放数字25
let name = "小明";    // 创建一个名为name的盒子,里面放文字"小明"
let is_student = true; // 创建一个名为is_student的盒子,里面放true
}

变量的可变性

Rust有一个重要的特点:默认情况下,变量一旦创建就不能改变内容。

#![allow(unused)]
fn main() {
let age = 25;
age = 26; // 错误!不能修改age的内容
}

这就像用胶水封住的盒子,一旦放好东西就不能再换了。

如果你需要一个可以改变内容的盒子,需要用mut关键字:

#![allow(unused)]
fn main() {
let mut age = 25;  // 创建一个可以修改的盒子
age = 26;          // 现在可以修改了!
age = 27;          // 还可以继续修改
}

为什么要这样设计?

这种设计有几个好处:

  1. 防止意外修改:避免程序中出现意外的数据变化
  2. 代码更清晰:一看就知道哪些数据会变,哪些不会变
  3. 编译器优化:Rust编译器可以更好地优化代码

与C语言的对比

如果你学过C语言,会发现Rust的做法很不同:

// C语言:变量默认可以修改
int age = 25;
age = 26; // 正常
#![allow(unused)]
fn main() {
// Rust:变量默认不能修改
let age = 25;
age = 26; // 编译错误!
}

小结

  • 变量就像盒子,用来存储数据
  • let创建变量
  • 默认情况下变量不能修改
  • mut创建可以修改的变量

6.2 基本数据类型

什么是数据类型?

就像现实世界中有不同的东西(数字、文字、真假),编程中也有不同的数据类型。每种类型都有特定的用途和特点。

Rust的基本数据类型

Rust提供了多种数据类型,让我们一一了解:

整型(整数)

整型用来存储整数,比如年龄、数量等:

#![allow(unused)]
fn main() {
let age: i32 = 25;           // 32位有符号整数,可以存储正数和负数
let count: u32 = 100;        // 32位无符号整数,只能存储正数
let small_number: u8 = 255;  // 8位无符号整数,范围0-255
}

整型类型表: | 类型 | 范围 | 用途 | |------|------|------| | i8 | -128 到 127 | 小整数 | | u8 | 0 到 255 | 小正数 | | i32 | -2,147,483,648 到 2,147,483,647 | 常用整数 | | u32 | 0 到 4,294,967,295 | 常用正数 | | i64 | 很大范围 | 大整数 | | u64 | 很大范围 | 大正数 |

浮点型(小数)

浮点型用来存储小数:

#![allow(unused)]
fn main() {
let pi: f64 = 3.14159;       // 64位浮点数,精度高
let price: f32 = 19.99;      // 32位浮点数,精度较低但节省内存
}

布尔型(真假)

布尔型只有两个值:true(真)和false(假):

#![allow(unused)]
fn main() {
let is_student = true;       // 是学生
let is_working = false;      // 不是在工作
}

字符型

字符型用来存储单个字符:

#![allow(unused)]
fn main() {
let grade = 'A';             // 字母
let emoji = '😊';            // 表情符号
let chinese = '中';          // 中文字符
}

注意: Rust的字符是Unicode字符,可以存储中文、表情符号等,而不仅仅是英文字母。

元组(组合数据)

元组可以同时存储多个不同类型的数据:

#![allow(unused)]
fn main() {
let person: (String, i32, bool) = ("小明".to_string(), 25, true);
// 包含:姓名、年龄、是否为学生

// 访问元组中的数据
let name = person.0;    // "小明"
let age = person.1;     // 25
let is_student = person.2; // true
}

数组(同类型数据列表)

数组用来存储多个相同类型的数据:

#![allow(unused)]
fn main() {
let scores: [i32; 5] = [85, 92, 78, 96, 88];  // 5个成绩
let first_score = scores[0];  // 85
let second_score = scores[1]; // 92
}

数组的特点:

  • 长度固定,创建后不能改变
  • 所有元素必须是相同类型
  • 用索引访问(从0开始)

类型推断

Rust很聪明,通常能自动判断数据类型:

#![allow(unused)]
fn main() {
let x = 42;        // Rust自动判断为i32
let y = 3.14;      // Rust自动判断为f64
let z = true;      // Rust自动判断为bool
}

但有时为了代码清晰,我们会显式标注类型:

#![allow(unused)]
fn main() {
let age: u8 = 25;  // 明确指定为u8类型
}

与C语言的对比

C语言Rust说明
inti3232位有符号整数
unsigned intu3232位无符号整数
charu8/charu8是字节,char是Unicode字符
floatf3232位浮点数
doublef6464位浮点数
数组[T; N]固定长度数组
结构体struct后续章节学习

6.3 常量与静态变量

什么是常量?

常量就像永远不变的盒子,一旦设置就不能修改。在Rust中,我们用const来定义常量:

#![allow(unused)]
fn main() {
const PI: f64 = 3.14159;           // 圆周率
const MAX_STUDENTS: u32 = 100;     // 最大学生数
const GREETING: &str = "你好!";    // 问候语
}

常量的特点:

  • 必须明确指定类型
  • 值必须在编译时确定
  • 不能修改
  • 通常用大写字母命名

静态变量

静态变量在整个程序运行期间都存在,用static定义:

#![allow(unused)]
fn main() {
static LANGUAGE: &str = "Rust";
static mut COUNTER: u32 = 0;  // 可变的静态变量(需要unsafe)
}

什么时候使用常量?

当你有一些永远不会改变的值时,比如:

  • 数学常数(π、e等)
  • 配置参数(最大连接数、超时时间等)
  • 固定的字符串

与普通变量的区别:

#![allow(unused)]
fn main() {
let normal_var = 42;        // 普通变量,可以修改(如果声明为mut)
const CONSTANT = 42;        // 常量,永远不能修改
}

与C语言的对比:

// C语言
#define PI 3.14159
const int MAX_SIZE = 100;
#![allow(unused)]
fn main() {
// Rust
const PI: f64 = 3.14159;
const MAX_SIZE: u32 = 100;
}

6.4 基本运算符和表达式

什么是运算符?

运算符就是用来进行各种运算的符号,比如加减乘除。让我们通过例子来学习:

算术运算符

#![allow(unused)]
fn main() {
let a = 10;
let b = 3;

let sum = a + b;        // 加法:10 + 3 = 13
let diff = a - b;       // 减法:10 - 3 = 7
let product = a * b;    // 乘法:10 × 3 = 30
let quotient = a / b;   // 除法:10 ÷ 3 = 3(整数除法)
let remainder = a % b;  // 取余:10 % 3 = 1
}

整数除法 vs 浮点除法:

#![allow(unused)]
fn main() {
let result1 = 10 / 3;   // 整数除法,结果是3
let result2 = 10.0 / 3.0; // 浮点除法,结果是3.333...
}

比较运算符

比较运算符用来比较两个值,结果总是truefalse

#![allow(unused)]
fn main() {
let x = 5;
let y = 10;

let is_equal = x == y;      // 等于:false
let is_not_equal = x != y;  // 不等于:true
let is_less = x < y;        // 小于:true
let is_greater = x > y;     // 大于:false
let is_less_equal = x <= y; // 小于等于:true
let is_greater_equal = x >= y; // 大于等于:false
}

逻辑运算符

逻辑运算符用来组合多个条件:

#![allow(unused)]
fn main() {
let is_student = true;
let is_working = false;

let both = is_student && is_working;  // 与:false(两个都为真才为真)
let either = is_student || is_working; // 或:true(有一个为真就为真)
let not_student = !is_student;        // 非:false(取反)
}

类型转换

Rust不会自动转换类型,需要手动转换:

#![allow(unused)]
fn main() {
let integer = 5;
let float = 2.5;

// 错误:不能直接相加不同类型的数
// let result = integer + float;

// 正确:先转换类型
let result = integer as f64 + float;  // 5.0 + 2.5 = 7.5
}

常用的类型转换:

  • as f64:转换为64位浮点数
  • as i32:转换为32位整数
  • as u8:转换为8位无符号整数

运算符优先级

就像数学中的运算顺序,Rust也有运算符优先级:

#![allow(unused)]
fn main() {
let result = 2 + 3 * 4;  // 先乘后加:2 + 12 = 14
let result2 = (2 + 3) * 4; // 先括号:5 * 4 = 20
}

优先级从高到低:

  1. 括号 ()
  2. 乘除取余 * / %
  3. 加减 + -
  4. 比较 == != < > <= >=
  5. 逻辑 && ||

与C语言的对比

运算C语言Rust说明
整数除法5 / 2 = 25 / 2 = 2相同
浮点除法5.0 / 2.0 = 2.55.0 / 2.0 = 2.5相同
类型转换自动转换需要asRust更严格
逻辑运算&& || !&& || !相同

6.5 实战练习

现在让我们动手实践,巩固学到的知识。每个练习都包含详细的步骤指导。

练习1:变量操作

目标: 学习如何声明和使用可变变量

步骤:

  1. 创建一个新的Rust项目:cargo new practice1
  2. src/main.rs中编写以下代码:
fn main() {
    // 声明一个可变的整型变量,初始值为10
    let mut number = 10;
    println!("初始值:{}", number);
    
    // 将其加5
    number = number + 5;
    println!("加5后:{}", number);
    
    // 再乘以2
    number = number * 2;
    println!("乘以2后:{}", number);
}

运行结果应该是:

初始值:10
加5后:15
乘以2后:30

练习2:使用元组

目标: 学习如何创建和使用元组

步骤:

  1. 创建新项目:cargo new practice2
  2. 编写代码:
fn main() {
    // 定义一个包含三种不同类型的元组
    let student = ("小明", 18, true);  // 姓名、年龄、是否为学生
    
    // 分别打印每个元素
    println!("姓名:{}", student.0);
    println!("年龄:{}", student.1);
    println!("是否为学生:{}", student.2);
    
    // 使用解构来获取元组中的值
    let (name, age, is_student) = student;
    println!("解构后 - 姓名:{},年龄:{},是否为学生:{}", name, age, is_student);
}

练习3:数组操作

目标: 学习如何创建数组并计算总和

步骤:

  1. 创建新项目:cargo new practice3
  2. 编写代码:
fn main() {
    // 创建一个长度为5的整型数组
    let scores = [85, 92, 78, 96, 88];
    
    // 计算所有元素的和
    let mut sum = 0;
    for score in scores.iter() {
        sum = sum + score;
    }
    
    // 计算平均值
    let average = sum as f64 / scores.len() as f64;
    
    println!("成绩:{:?}", scores);
    println!("总分:{}", sum);
    println!("平均分:{:.2}", average);
}

练习4:类型转换

目标: 学习如何处理类型转换

步骤:

  1. 创建新项目:cargo new practice4
  2. 先尝试错误的代码(会被编译器阻止):
fn main() {
    let integer: i32 = 42;
    let float: f64 = 3.14;
    
    // 这行代码会编译错误,取消注释试试看
    // let result = integer + float;
    
    // 正确的做法:使用as进行类型转换
    let result = integer as f64 + float;
    println!("{} + {} = {}", integer, float, result);
    
    // 更多类型转换的例子
    let small_int: u8 = 255;
    let large_int: u32 = small_int as u32;
    println!("{} 转换为 u32 后:{}", small_int, large_int);
}

练习5:综合应用

目标: 综合运用本章学到的所有知识

步骤:

  1. 创建新项目:cargo new practice5
  2. 编写一个简单的学生成绩计算程序:
fn main() {
    // 定义常量
    const PASS_SCORE: f64 = 60.0;
    
    // 学生信息(姓名,数学成绩,英语成绩,是否缺考)
    let student1 = ("张三", 85.5, 92.0, false);
    let student2 = ("李四", 78.0, 88.5, false);
    let student3 = ("王五", 0.0, 0.0, true);  // 缺考
    
    // 计算每个学生的平均分
    let students = [student1, student2, student3];
    
    for student in students.iter() {
        let (name, math, english, absent) = student;
        
        if *absent {
            println!("{}:缺考", name);
        } else {
            let average = (math + english) / 2.0;
            let status = if average >= PASS_SCORE { "及格" } else { "不及格" };
            println!("{}:数学{},英语{},平均{:.1},{}", 
                     name, math, english, average, status);
        }
    }
}

运行结果应该是:

张三:数学85.5,英语92.0,平均88.8,及格
李四:数学78.0,英语88.5,平均83.3,及格
王五:缺考

练习提示

  • 如果遇到编译错误,仔细阅读错误信息,编译器会告诉你问题在哪里
  • 可以尝试修改代码中的值,看看结果会有什么变化
  • 不要害怕犯错,编程就是在不断试错中学习的

6.6 小结与思考

恭喜你!你已经完成了Rust基础语法的学习。让我们来回顾一下这一章学到了什么:

本章要点总结

1. 变量就像盒子

  • let创建变量(盒子)
  • 默认情况下盒子是封住的(不可变)
  • mut创建可以打开的盒子(可变)

2. 数据类型就像不同的东西

  • 整型:整数(年龄、数量)
  • 浮点型:小数(价格、分数)
  • 布尔型:真假(是否、开关)
  • 字符型:单个字符(字母、符号、中文)
  • 元组:组合数据(姓名+年龄+状态)
  • 数组:同类型列表(成绩单、购物清单)

3. 运算符就像数学符号

  • 算术:+ - * / %
  • 比较:== != < > <= >=
  • 逻辑:&& || !
  • 类型转换:as

4. 常量是永远不变的盒子

  • const定义
  • 必须明确类型
  • 通常用大写字母命名

与C语言的主要区别

特点C语言Rust好处
变量默认可变不可变更安全
类型转换自动手动更明确
字符类型1字节4字节Unicode支持中文
数组长度可变固定更安全

思考题

初级思考:

  1. 为什么Rust默认变量不可变?这样设计有什么好处?
  2. 你能举出生活中哪些东西是"不可变的"吗?(比如生日、性别)

中级思考: 3. Rust的char和C语言的char有什么不同?为什么这样设计? 4. 什么时候应该使用常量而不是普通变量?

高级思考: 5. 如果让你设计一个学生管理系统,你会用哪些数据类型来存储学生信息? 6. 为什么Rust不自动进行类型转换?这样设计有什么优缺点?