03 - Rust函数
前言
函数是编程编程语言中的基本模块,通过对代码块进行封装成函数,实现特定功能。对外仅提供函数名作为接口,方便外部调用。
函数本身就是一种类型,函数类型变量可以作为其他函数的参数或者返回值,也可以赋值给别的变量,还可以直接调用执行。
一、函数定义
Rust 使用fn关键字定义函数。函数的定义使用fn关键字,后跟函数名、参数列表、返回类型和函数体。
函数体包含于{}内,是函数要执行的具体代码。
fn <函数名> ( <参数> ) <函数体> { }
如下函数实例,定义了一个名为another_function的无参、无返回值的函数。
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Hello, nhooo!");
}
1.1 函数参数
函数参数必须明确指定数据类型,但不能指定默认值。
Rust 中定义函数如果需要具备参数必须声明参数名称和类型。
fn main() {
another_function(2, 3);
}
fn another_function(x: i32, y: i32) {
println!("x 的值为 : {}", x);
println!("y 的值为 : {}", y);
}
/*输出
x 的值为 : 5
y 的值为 : 6
*/
函数参数可以分为可变和不可变参数,默认不可变参数,需要可变操作时,需要加上mut关键字。
fn add(mut x: i32, y:i32) -> i32 {
x = x + 1;
return x + y;
}
fn main() {
let x = 5;
let y = {
let x = 2;
x + 1 // 结尾没有分号
};
let sum = add(x,y);
println!("{} + {} = {}",x,y,sum);
}
1.2 函数体语句和表达式
main函数
:main函数是程序的入口函数,对于可执行文件来说,main函数是必不可少的,对于库函数来说,main函数不是必须的。
函数体
: 函数体由一系列语句和一个可选的结尾表达式构成。
结尾没有分号,代表这是一个表达式而非语句,将会自动返回表达式的值,表示式的结尾如果加上分号,就变成了语句,语句没有返回值。
Rust 中可以在一个用 {} 包括的块里编写一个较为复杂的表达式:
fn add(x: i32, y:i32) -> i32 {
x+y // 结尾没有分号
}
fn main() {
let x = 5;
let y = {
let x = 2;
x + 1 // 结尾没有分号
};
let sum = add(x,y);
println!("{} + {} = {}",x,y,sum);
}
//5 + 3 = 8
这段程序中包含了一个表达式块:
{
let x = 3;
x + 1
};
而且在块中可以使用函数语句,最后一个步骤是表达式,此表达式的结果值是整个表达式块所代表的值。这种表达式块叫做函数体表达式。
注意:x + 1 之后没有分号,否则它将变成一条语句!
这种表达式块是一个合法的函数体。而且在 Rust 中,函数定义可以嵌套:
示例:
fn main() {
fn five() -> i32 {
5
}
println!("five() 的值为: {}", five());
}
1.3 函数返回值
如果函数需要返回值给调用者,在函数定义时需要明确返回值类型。
使用 -> 数据类型来定义
函数只能有一个返回值,需要返回多个值时,可以使用元组类型。
Rust中每个函数都有返回值,即使没有显示返回值的函数,也会隐式地返回一个单元值()
一般,函数隐式地返回函数体最后一个表达式的值。
上一个嵌套函数示例已经显示了函数的返回值定义方式,
Rust 函数声明返回值类型的方式:在参数声明之后用 -> 来声明函数返回值的类型。
在函数体中,随时都可以以 return 关键字结束函数运行并返回一个类型合适的值。
示例:
fn add(a: i32, b: i32) -> i32 {
// a + b //函数体表达式
return a + b;
}
Rust 不支持自动返回值类型判断!如果没有明确声明函数返回值的类型,函数将被认为是"纯过程",不允许产生返回值,return 后面不能有返回值表达式。这样做的目的是为了让公开的函数能够形成可见的公报。
注意:函数体表达式并不能等同于函数体,它不能使用 return 关键字。
1.4 函数作为参数和返回值
在Rust中,函数可以作为参数传递给其他函数,也可以作为函数的返回值。这种特性可以实现函数的灵活组合和高阶函数的编写。
以下是一个函数作为参数和返回值的示例:
fn add(a: i32, b: i32) ->i32 {
return a + b;
}
fn mult(a: i32, b: i32) ->i32 {
return a * b;
}
fn calc(function: fn(i32, i32)->i32, a: i32, b: i32) -> i32 {
return function(a,b);
}
fn main(){
let result = calc(add, 100,100);
println!("result = {}", result);
let result = calc(mult, 100,200);
println!("result = {}", result);
}
//result = 200
//result = 20000
在上述示例中,定义了两个简单的函数add和mult,分别用完成两个参数的相加和相乘操作。
然后定义了一个名为calc的函数,它接收一个函数参数function,类型为fn(i32, i32) -> i32,表示接收两个i32类型参数并返回i32类型结果的函数。在函数体中,我们调用了function函数,并传递了a和b作为参数。
在main函数中分别使用add和mult作为calc函数的参数,并打印出计算结果。
1.5 方法
方法和函数类似,都是使用fn
关键字定义,可以有参数、返回值、函数体。
结构体的方法必须在结构体的上下文中定义,也就是定义在impl块中。
在impl块中定义的不一定是方法,也有可能是关联函数。
方法要求第一个参数必须是self, 代表调用该方法的结构体实例。
在方法中使用&self能够读取实例中的数据,使用&mut self能够向实例中写入数据。
使用方法替代函数的最大好处在于组织性,将结构体实例的所有行为都一起放入impl块中
关联函数是指在impl块中定义,但是不以self作为参数的函数,与结构体相关联,但是不直接作用于结构体实例,常用作返回一个结构体实例的构造函数。
调用:
在方法内部可以使用self.字段名 来访问结构体的字段
在方法外使用实例名.方法名 调用方法
调用关联函数使用 结构体名::关联函数名
在调用结构体方法时,第一个参数self不需要传递实参,由Rust编译器完成
pub struct Student {
name :&'static str,
score : i32,
}
impl Student {
pub fn new(name : &'static str,socre:i32) ->Self{
Student{name,socre}
}
pub fn get_name(&self) -> &str {
self.name
}
}
fn main(){
let mut student : Student = Student::new("wkk",22);
println!("{:?}",student);
}
1.6 高阶函数
以函数为参数或者返回值的函数。
函数是一种类型,函数类型的变量可以像其他类型的变量一样使用,可以被直接调用执行,也可以作为其他函数的参数或者返回值。
实现这一切的基础是函数指针,函数指针类型使用fn() 来指定
1.7 函数指针
指向函数的指针,值是函数的地址
声明中必须显示指定函数后指针的类型fn()
fn hello(){
println!("hello Rust");
}
fn main(){
let fn_ptr: fn() = hello;
fn_ptr();
let other_fn = hello; // 函数hello本身的类型 , other_fn 不是函数指针类型
}
//hello Rust
函数指针和闭包都可以用来表示可调用对象,但它们之间有一些重要的区别。其中一个区别是,闭包可以捕获其周围环境中的变量,而函数指针则不能。
不同于闭包,fn 是一个类型而不是一个 trait,所以直接指定 fn 作为参数而不是声明一个带有 Fn 作为 trait bound 的泛型参数。
函数指针实现了所有三个闭包 trait(Fn、FnMut 和 FnOnce),所以总是可以在调用期望闭包的函数时 传递函数指针作为参数。
倾向于编写使用泛型和闭包 trait 的函数,这样它就能接受函数或闭包作为参数。 一个只期望接受 fn 而不接受闭包的情况的例子是与不存在闭包的外部代码交互时:C 语言的函数可以接受函数作为参数,但 C 语言没有闭包。
函数指针的应用场景:
可以作为参数传递给其他函数,以便在函数内部调用。这在一些高阶函数(higher-order functions)中非常常见,例如 map 和 filter 等。
函数指针还可以用于定义回调函数(callback functions),例如在事件驱动编程(event-driven programming)中。
函数指针还可以存储在数据结构中,以便稍后调用。这在一些算法中非常有用,例如排序算法。
函数指针的优缺点
函数指针的优点之一是它们没有运行时开销。这意味着它们可以在不影响性能的情况下用于表示可调用对象。
但是,函数指针也有一些局限性。例如,它们不能捕获其周围环境中的变量,这使得它们不如闭包灵活。此外,函数指针只能指向那些在编译时已知的函数,这意味着它们不能用于表示匿名函数。
总结
本章学习在Rust中函数的使用方法。包括无参函数、有参函数、函数返回值、函数体语句和表达式、函数作为参数和返回值、方法以及函数指针的用法。