点击上方蓝字 江湖评谈设为关注/星标
闭包
Rust闭包分为借用所有权和获取所有权两个概念,借用即是闭包跟原始程序段借用它的数据,借用了之后原始程序段数据依然可用。获取即闭包跟原始程序段获取它的数据,获取了之后让原始程序段数据失效。获取权的好处是,可以让闭包独立于原始程序段,或者不受制于原始程序段的干扰,独立执行。
借用(注意||[表示闭包]表示没有参数,默认闭包借用)所有权:
fn main() {
let x = String::from("Hello");
let closure =|| {
println!("{}", x);
};
// 这里 x 已经被闭包借用,依然可以使用
println!("{}", x); // 这行代码会导致编译错误
closure(); // 现在闭包可以使用 x 的所有权来打印内容
}
获取(mov关键字闭包获取)所有权
fn main() {
let x = String::from("Hello");
let closure =mov || {
println!("{}", x);
};
// 这里 x 已经被闭包捕获并移走,不能再在此作用域使用
println!("{}", x); // 这行代码会导致编译错误
closure(); // 现在闭包可以使用 x 的所有权来打印内容
}
因为闭包获取了所有权,所以导致了原始程序段失效,编译的时候报错:
error[E0425]: cannot find value `mov` in this scope
--> rustfirstproj.rs:3:18
|
3 | let closure =mov || {
| ^^^ not found in this scope
带参数的闭包:
let add = |a, b| a + b;
println!("{}", add(2, 3)); // 输出: 5
Rust-main
上一篇:Rust编译器研究+.NET9 PreView7 ,提到rust-main从glibc->lang_start->lang_start_internal这个过程,但实际上lang_start_internal里面通过闭包获取所有权调用的rust-main:
fn lang_start_internal(
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
argc: isize,
argv: *const *const u8,
sigpipe: u8,
) -> Result<isize, !> {
use crate::{mem, panic};
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
};
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtabort!("drop of the panic payload panicked");
});
panic::catch_unwind(cleanup).map_err(rt_abort)?;
ret_code
}
panic概念,在Rust中,panic 是一种用于处理不可恢复错误的机制。当程序遇到无法继续执行的严重错误时,Rust会触发panic,使程序立即停止执行并开始展开(unwind)或直接终止(abort)。
这里需要注意:Rust通过Result
和Option
类型处理可恢复错误,通过panic
处理不可恢复错误。
panic::catch_unwind 是 Rust 标准库中提供的一个函数,用于捕获在 Rust 代码中发生的 panic。它允许你在程序发生 panic 时,进行某种形式的错误恢复或处理,而不是让程序直接崩溃。
use std::panic;
fn main() {
let result = panic::catch_unwind(|| {
println!("This is a safe block.");
panic!("Something went wrong!");
});
match result {
Ok(_) => println!("No panic occurred."),
Err(err) => println!("Panic occurred: {:?}", err),
}
}
上面代码引发了panic,被catch_unwind捕获,并将其作为 Err 返回。match 表达式用于检查 catch_unwind 的结果并进行相应的处理。
回到lang_start_internal-》catch_unwind
首先从当前模块(create)
use crate::{mem, panic};
crate代表当前包(或库)。在Rust中,crate可以理解为一个独立的编译单元或包.
通常,一个crate对应一个Cargo.toml文件以及它所管理的源码。
当你使用crate::some_module时,实际上是在引用当前包中的some_module模块。
mem和panic都是从当前包的根模块(crate)中导入的模块或函数。
这意味着这两个模块或函数是在当前包中定义的,而不是标准库或外部库的一部分。
这行代码的意思:
这行代码使用了Rust的use关键字,目的是从当前模块的crate(也就是当前包)
中导入mem和panic两个模块或函数
闭包的获取,以便后面用到(因为是闭包获取。注意此时rtabort!("initialization or cleanup bug")是失效的,所有权在rt_abort)。
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
};
mem::forget(e) 会丢弃错误 e 而不运行其析构函数。
这意味着错误值不会被释放或清理。这种操作通常用于防止内存被自动释放,
可能是为了避免一些析构函数中的副作用(如在 panic 过程中再次 panic)。
rtabort! 是一个宏,用于立即终止程序,并打印一条错误消息 "drop of the panic payload panicked"。
这是在处理 panic 时再次发生 panic 的一种防御性措施。
闭包获取所有权,里面调用了unsafe不安全代码init,以及发生错误的时候处理。这里的rt_abort即上面的闭包获取所有权的rt_abort。
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
unwrap_or
对于 Option<T>,.unwrap_or(default) 方法的作用是:
如果 Option 是 Some(T),则返回 T 的值。
如果 Option 是 None,则返回提供的 default 值。
继续,重点调用rust-main来了。通过catch_unwind捕获main调用异常,如果没有异常正常返回,如果有异常返回unwrap_or的参数101。外面再嵌套一层catch_unwind,通过map_err闭包获取所有权继续处理异常。可见它这里调用main是非常小心的。
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtabort!("drop of the panic payload panicked");
});
调用main完成之后,进行资源清理:
panic::catch_unwind(cleanup).map_err(rt_abort)?;
这里的cleanup是rust函数:
pub(crate) fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
// Flush stdout and disable buffering.
crate::io::cleanup();
// SAFETY: Only called once during runtime cleanup.
sys::cleanup();
});
}
结尾
Rust的语法方面可见简洁度真的是提高了不少
往期精彩回顾