Skip to content

基本语法

本章将介绍 Rust 的基本语法,包括变量、数据类型、函数和注释。这些是学习任何编程语言的基础。

在 Rust 中,变量默认是不可变的(immutable):

fn main() {
let x = 5;
println!("x 的值是: {}", x);
// 下面这行会报错!
// x = 6; // error: cannot assign twice to immutable variable
}

这是 Rust 保证安全性的重要特性之一。

如果需要修改变量,使用 mut 关键字:

fn main() {
let mut x = 5;
println!("x 的值是: {}", x);
x = 6; // 现在可以修改了
println!("x 的值是: {}", x);
}

可以用相同的名字声明新变量,新变量会”遮蔽”旧变量:

fn main() {
let x = 5;
let x = x + 1; // 遮蔽,创建新变量
let x = x * 2; // 再次遮蔽
println!("x 的值是: {}", x); // 输出: 12
}

遮蔽与 mut 的区别:

  • 遮蔽创建新变量,可以改变类型
  • mut 只能修改值,不能改变类型
// 遮蔽可以改变类型
let spaces = " "; // &str 类型
let spaces = spaces.len(); // usize 类型,合法
// mut 不能改变类型
let mut spaces = " ";
// spaces = spaces.len(); // 错误!类型不匹配

常量使用 const 声明,必须标注类型,且必须是编译时常量:

const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159265358979;

常量与不可变变量的区别:

  • 常量必须标注类型
  • 常量必须是编译时可确定的值
  • 常量可以在任何作用域声明,包括全局
  • 常量命名约定:全大写,下划线分隔

Rust 是静态类型语言,编译时必须知道所有变量的类型。

长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

isizeusize 的大小取决于运行程序的计算机架构(32位或64位)。

fn main() {
let a: i32 = 42;
let b: u8 = 255;
let c = 98_222; // 可以用下划线增加可读性
let d = 0xff; // 十六进制
let e = 0o77; // 八进制
let f = 0b1111_0000; // 二进制
let g = b'A'; // 字节(仅限 u8)
}
fn main() {
let x = 2.0; // f64(默认)
let y: f32 = 3.0; // f32
}
fn main() {
let t = true;
let f: bool = false;
}

Rust 的 char 是 4 字节的 Unicode 标量值:

fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
println!("字符大小: {} 字节", std::mem::size_of::<char>()); // 4
}

元组可以包含不同类型的值,长度固定:

fn main() {
// 创建元组
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
println!("y 的值是: {}", y);
// 索引访问(从 0 开始)
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
}

空元组 () 称为”单元类型”,表示空值或空返回类型。

数组的所有元素必须是相同类型,长度固定:

fn main() {
// 创建数组
let a = [1, 2, 3, 4, 5];
let b: [i32; 5] = [1, 2, 3, 4, 5]; // 显式类型标注
let c = [3; 5]; // [3, 3, 3, 3, 3],5 个 3
// 访问元素
let first = a[0];
let second = a[1];
// 数组长度
println!("数组长度: {}", a.len());
}

注意:访问越界会导致运行时 panic:

fn main() {
let a = [1, 2, 3];
// let element = a[10]; // 运行时 panic!
}
fn main() {
println!("Hello from main!");
another_function();
}
fn another_function() {
println!("Hello from another function!");
}

Rust 不关心函数定义的顺序。

fn main() {
print_value(5);
print_labeled_value(5, 'h');
}
fn print_value(x: i32) {
println!("x 的值是: {}", x);
}
fn print_labeled_value(value: i32, unit: char) {
println!("值是: {}{}", value, unit);
}

注意:函数参数必须声明类型。

使用 -> 声明返回类型,最后一个表达式的值作为返回值:

fn five() -> i32 {
5 // 注意:没有分号,这是表达式
}
fn plus_one(x: i32) -> i32 {
x + 1
}
fn main() {
let x = five();
let y = plus_one(5);
println!("x = {}, y = {}", x, y);
}

也可以使用 return 提前返回:

fn abs(x: i32) -> i32 {
if x < 0 {
return -x;
}
x
}
  • 语句(Statement):执行动作,不返回值
  • 表达式(Expression):计算并产生值
fn main() {
// 语句
let x = 5; // let 语句不返回值
// 表达式
let y = {
let x = 3;
x + 1 // 注意没有分号,这是表达式
};
println!("y = {}", y); // 输出: 4
}

重要:加上分号就变成了语句,不返回值:

fn main() {
let y = {
let x = 3;
x + 1; // 加了分号,变成语句,返回 ()
};
// y 的类型是 (),不是 i32
}
// 这是单行注释
/*
* 这是多行注释
* 可以跨越多行
*/

文档注释用于生成 API 文档:

/// 计算两个数的和
///
/// # 参数
///
/// * `a` - 第一个数
/// * `b` - 第二个数
///
/// # 示例
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}

运行 cargo doc --open 可以生成并查看文档。

尝试编译以下代码,观察错误信息:

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

修复它,使程序能够正常运行。

编写一个函数 add,接收两个 i32 参数,返回它们的和:

fn add(a: i32, b: i32) -> i32 {
// 你的代码
}
fn main() {
let result = add(3, 5);
println!("3 + 5 = {}", result);
}

创建一个元组存储一个人的信息:姓名(String)、年龄(u8)、身高(f32),然后解构并打印:

fn main() {
let person = (String::from("张三"), 25, 175.5);
// 解构并打印
}

创建一个长度为 5 的整数数组,编写代码计算所有元素的平均值:

fn main() {
let numbers = [10, 20, 30, 40, 50];
// 计算并打印平均值
}

提示:注意整数除法和浮点数除法的区别。

掌握了基本语法后,下一章我们将学习 Rust 的控制流:条件判断和循环。