5. 函数
5.1 简介
在日常生活中,面对复杂任务,人们通常会将其拆分为若干个简单任务,化繁为简逐步完成目标。程序设计也遵循这一思想:将复杂功能拆分为多个函数,分别负责不同的任务,而不是将所有逻辑集中在 main 函数中。这样不仅可以使程序结构更加清晰,还能复用已有函数,提升代码的可读性和可维护性。
5.2 函数声明
函数的定义语法形式为 fn 函数名(参数列表) -> 返回类型 { ... }。其中,fn 是函数声明关键字,函数名用于标识函数本身,参数列表用于定义函数接收的输入数据及其类型,返回类型用于指定函数输出的数据类型。参数列表和返回类型均为可选项,函数可以根据需求选择是否接收参数以及是否返回结果。
// 1. 无参数、无返回值的函数
fn say_hello() {
println!("Hello, Rust!");
}
// 2. 带参数的函数,参数可以有多个,多个参数之间以逗号分割,函数中可以像变量一样使用函数参数
fn print_sum(a: i32, b: i32) {
println!("sum = {}", a + b);
}
// 3. 带参数和返回值的函数
fn add(a: i32, b: i32) -> i32 {
return a + b; // 使用 return 关键字返回计算结果
}
fn main() {
say_hello();
print_sum(3, 5);
let result = add(10, 20);
println!("result = {}", result);
}
shell> cargo run
Hello, Rust!
sum = 8
result = 30
5.3 函数返回值
在 Rust 中,如果函数没有显式指定返回类型,则默认返回单元类型 ()。函数可以使用 return 关键字显式返回值,或省略 return 关键字,直接将函数体最后一个表达式的值作为返回值。
use std::any::type_name_of_val;
fn foo() {
// 没有显式指定返回值,默认返回单元类型 ()
}
fn add(a: i32, b: i32) -> i32 {
// 使用 return 关键字显式指定返回值
return a + b;
}
fn multiply(a: i32, b: i32) -> i32 {
// 省略 `return` 关键字,将函数体最后一个表达式的值作为返回值
a * b
}
fn main() {
let f = foo();
println!("{}", type_name_of_val(&f)); // 输出:()
let sum = add(10, 20); // 输出:30
println!("sum = {}", sum);
let product = multiply(10, 20); // 输出:200
println!("product = {}", product);
}
shell> cargo run
()
sum = 30
product = 200
5.4 函数并联数据类型
函数可与数据类型关联,将数据及其相关行为组织在一起,从而提升代码的可读性与可维护性。在 Rust 中,可以通过 impl 数据类型 块为类型定义关联函数,仅与数据类型本身关联、不依赖具体实例数据的函数称为静态函数;需要通过具体实例访问数据的函数称为实例函数。在下述示例中,为表示矩形的结构体定义了一个静态函数和三个实例函数。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 静态函数:通过类型名称奶即可调用,不依赖实例
fn describe() {
println!("Rectangle 类型用于表示一个矩形,支持计算面积和周长。");
}
// 实例函数:&self 表示不可变引用,函数只能读取数据,不能修改
fn area(&self) -> u32 {
self.width * self.height
}
// 实例函数:&mut self 表示可变引用,函数可以修改实例数据
fn set_width(&mut self, width: u32) {
self.width = width;
}
// 实例函数:self 表示获取所有权,调用后实例无法继续使用
fn perimeter(self) -> u32 {
2 * (self.width + self.height)
}
}
fn main() {
// 静态函数:通过类型名调用,不需要实例,调用格式为 类型名::函数名
Rectangle::describe();
// 创建实例并初始化数据
let mut rect = Rectangle { width: 10, height: 20 };
// 调用不可变引用的实例函数:只读取数据
let area = rect.area();
println!("面积: {area}");
// 调用可变引用的实例函数:修改实例的数据
rect.set_width(15);
println!("修改后宽度: {}", rect.width);
// 调用获取所有权的函数,调用后实例不可再使用
let perimeter = rect.perimeter();
println!("周长: {perimeter}");
// 下面的代码会编译报错,因为 `rect` 的所有权已被转移
// println!("{}", rect.width); // 错误:实例的所有权已被转移
}
shell> cargo run
Rectangle 类型用于表示一个矩形,支持计算面积和周长。
面积: 200
修改后宽度: 15
周长: 70
除结构体外,枚举类型同样可定义关联函数,将其数据表示与相关操作组织在一起,使得枚举不仅可以描述各变体的状态,还能提供处理这些状态的函数。
// 定义枚举
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
impl Shape {
// 针对不同的枚举变体,采用不同的面积计算方式
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => 3.14 * r * r,
Shape::Rectangle(w, h) => w * h,
}
}
}
fn main() {
// 创建枚举实例
let rectangle = Shape::Rectangle(10.0, 5.0);
println!("矩形面积: {}", rectangle.area());
let circle = Shape::Circle(10.0);
println!("圆面积: {}", circle.area());
}
shell> cargo run
矩形面积: 50
圆面积: 314