目录

rust学习笔记14-函数

目录

rust学习笔记14-函数

rust函数在前边已经接触很多了,现在做全面总结 1.函数定义 在 Rust 中,使用 fn 关键字来定义函数。函数可以有参数、返回值,并且可以包含任意数量的语句。 fn function_name(parameter_list) -> ReturnType { // 函数体 // 返回值(可选) } 2.返回值 Rust 函数的返回值类型需要显式声明。如果函数没有返回值,则返回类型为 (),即单元类型。 隐式返回:如果函数的最后一行表达式没有分号,它将被视为返回值。 显式返回:使用 return 关键字返回一个值。 // 隐式返回 fn add(a: i32, b: i32) -> i32 { a + b } // 显式返回 fn subtract(a: i32, b: i32) -> i32 { return a - b; } fn main() { let sum = add(5, 3); let difference = subtract(5, 3); println!(“Sum: {}, Difference: {}”, sum, difference); } 3.函数参数 (1)函数值参数传递(Move) 函数的代码本身通常是存储在可执行文件的代码段,而在调用时函数会在栈上开辟一个新的stack frame(栈空间),用于存储函数的局部变量、参数和返回地址等信息,而当函数结束后会释放该空间。 而当传入non-Copy value(Vec、String等)传入函数时实参会转移value的所有权给形参,实参会失去value的所有权而在函数结束时,value的所有权会释放 https://i-blog.csdnimg.cn/direct/d5df3f150feb48cba862552203721096.png (2)不可变借用 如果你不想失去value的所有权,你又没有修改value的需求,你可以使用不可变借用 在 Rust 中,你可以将不可变引用作为函数的参数,从而在函数内部访问参数值但不能修改它。这有助于确保数据的安全性,防止在多处同时对数据进行写操作,从而避免数据竞争。 如何应用不可变借用,通过Use * to deference,去获取其的值 fn borrow_test(s: &String){ println!("{}", (*s).to_uppercase()); } fn main() { let b = String::from(“rust”); borrow_test(&b); println!("{}", b); https://i-blog.csdnimg.cn/direct/239a0030d24b4cc2a4d20d604c2a50f5.png (3)可变借用 如何你有修改值的需求你可以使用可变借用,以允许在函数内部修改参数的值。以允许在函数内部修改参数的值。但在同一时间内只能有一个可变引用。 需要再形参前加&mut 如何应用可变借用,还是通过Use * to deference,去获取其的值 fn increment_by_reference(x: &mut i32) { *x += 1; // 修改引用指向的值 } fn main() { let mut num = 5; increment_by_reference(&mut num); println!("{}", num); // 输出:6 } 4.递归函数 递归函数通常包含两个主要部分: 基准条件(Base Case):递归终止的条件,避免无限递归。 递归步骤(Recursive Step):将问题分解为更小的子问题,并调用自身来解决这些子问题。 //阶乘递归 fn factorial(n: u32) -> u32 { if n == 0 { 1 // 基准条件 } else { n * factorial(n - 1) // 递归步骤 } } fn main() { let result = factorial(5); println!(“5的阶乘:{}”, result); } https://i-blog.csdnimg.cn/direct/48167612abf942e1a2bbfb10a0345d4a.png 5.生命周期与函数 在 Rust 中,生命周期(lifetimes)是一种编译时检查机制,用于确保引用始终有效。Rust 编译器使用生命周期注解来验证引用不会超出其指向的数据的有效范围,从而避免悬垂指针和其他内存安全问题。理解生命周期的概念和用法是编写安全且高效的 Rust 代码的关键。 为什么需要生命周期? Rust 的所有权系统确保每个值都有一个明确的所有者,并且当所有者离开作用域时,该值会被自动释放。然而,当涉及到引用时,编译器需要一种方法来确保引用在其所指向的数据被释放之前保持有效。这就是生命周期的作用。 生命周期的基本概念 生命周期是一个抽象的概念,表示引用的有效范围。生命周期注解通过撇号 ‘a 来表示,其中 ‘a 是一个标识符,可以是任意的,但通常使用 ‘a, ‘b, ‘c 等。 fn longest<‘a>(x: &‘a str, y: &‘a str) -> &‘a str { if x.len() > y.len() { x } else { y } } 生命周期省略规则 Rust 编译器有一些内置的生命周期省略规则,允许在某些情况下省略显式的生命周期注解。这些规则适用于简单的情况,使代码更加简洁。 编译器在没有显式注解的情况下,使用三个规则来推断这些生命周期 第一个规则是每个作为引用的参数都会得到它自己的生命周期参数。 第二个规则是,如果只有一个输入生命周期参数,那么该生命周期将被分配给所有输出生命周期参数(该生命周期将分配给返回值) 第三个规则是,如果有多个输入生命周期参数,但其中一个是对 self 或不可变 self 的引用时。因为在这种情况下它是一个方法,所以 self 的生命周期被分配给所有输出生命参数 //规则一 每个引用参数都有其自己的生命周期 foo等价foo2 fn foo(x: &i32) -> &i32 { x } fn foo2<‘a>(x: &‘a i32) -> &‘a i32 { x } //规则二 如果只有一个输入引用,则输出引用与该输入引用具有相同的生命周期 fn first_word(s: &str) -> &str { // 假设这是一个实现 “example” } //规则三 如果有多个输入引用,则第一个输入引用决定输出引用的生命周期 //这个函数会导致编译错误,因为编译器无法确定返回值的生命周期应该与 x 还是 y 相同。因此,需要显式地添加生命周期注解:如longest2 fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } fn longest2(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } 总结,函数是每个编程语言最重要内容,在rust中,函数参数分所有权移动(Move)、不可变引用、可变引用需要重点理解掌握。生命周期 是 Rust 编译器用来确保引用在其所指向的数据被释放之前保持有效的机制。生命周期注解 使用撇号 ‘a 表示,帮助编译器验证引用的有效性。