Rust 中类似 Golang 的 defer

在 Go 中,您可以使用defer关键字在当前函数返回时执行函数,类似于finally其他语言中的传统关键字。无论整个函数体发生什么,这对于清理状态都很有用。这是 Go 博客中的一个示例:


func CopyFile(dstName, srcName string) (written int64, err error) {

    src, err := os.Open(srcName)

    if err != nil {

        return

    }

    defer src.Close()


    dst, err := os.Create(dstName)

    if err != nil {

        return

    }

    defer dst.Close()


    return io.Copy(dst, src)

}

如何在 Rust 中实现这个功能?我知道 RAII,但在我的特定情况下,状态在外部系统中。我正在编写一个将键写入键值存储的测试,我需要确保它在测试结束时被删除,无论测试中的断言是否导致恐慌。


我找到了这个要点,但我不知道这是否是推荐的方法。不安全的析构函数令人担忧。


Rust GitHub 存储库上也有这个问题,但它已经三年了,显然不再重要了。


aluckdog
浏览 436回答 2
2回答

大话西游666

(e:不要错过下面的 bluss 的回答和他们的scopedguard crate。)通过让代码在析构函数中运行来实现这一点的正确方法,就像defer!您链接到的宏一样。除了临时测试之外,我建议使用适当的析构函数编写句柄类型,例如std::sync::Mutex通过其MutexGuard类型(由 返回lock)进行交互:无需调用unlock互斥锁本身。(显式使用析构函数处理的方法也更灵活:它可以对数据进行可变访问,而延迟方法可能不能,因为 Rust 强大的别名控制。)无论如何,由于最近的更改,该宏现在(很多!)改进了,特别是 pnkfelix 的声音通用删除工作,这消除了#[unsafe_destructor]. 直接更新将是:struct ScopeCall<F: FnMut()> {&nbsp; &nbsp; c: F}impl<F: FnMut()> Drop for ScopeCall<F> {&nbsp; &nbsp; fn drop(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; (self.c)();&nbsp; &nbsp; }}macro_rules! defer {&nbsp; &nbsp; ($e:expr) => (&nbsp; &nbsp; &nbsp; &nbsp; let _scope_call = ScopeCall { c: || -> () { $e; } };&nbsp; &nbsp; )}fn main() {&nbsp; &nbsp; let x = 42u8;&nbsp; &nbsp; defer!(println!("defer 1"));&nbsp; &nbsp; defer!({&nbsp; &nbsp; &nbsp; &nbsp; println!("defer 2");&nbsp; &nbsp; &nbsp; &nbsp; println!("inside defer {}", x)&nbsp; &nbsp; });&nbsp; &nbsp; println!("normal execution {}", x);}输出:normal execution 42defer 2inside defer 42defer 1虽然,它在语法上会更好,因为:macro_rules! expr { ($e: expr) => { $e } } // tt hackmacro_rules! defer {&nbsp; &nbsp; ($($data: tt)*) => (&nbsp; &nbsp; &nbsp; &nbsp; let _scope_call = ScopeCall {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c: || -> () { expr!({ $($data)* }) }&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; )}(tt hack由于#5846 ,这是必要的。)泛型tt(“令牌树”)的使用允许{ ... }在有多个语句时无需内部调用它(即它的行为更像“正常”控制流结构):defer! {&nbsp; &nbsp; println!("defer 2");&nbsp; &nbsp; println!("inside defer {}", x)}此外,为了获得延迟代码可以对捕获的变量执行的操作的最大灵活性,可以使用FnOnce代替FnMut:struct ScopeCall<F: FnOnce()> {&nbsp; &nbsp; c: Option<F>}impl<F: FnOnce()> Drop for ScopeCall<F> {&nbsp; &nbsp; fn drop(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.c.take().unwrap()()&nbsp; &nbsp; }}这还需ScopeCall要用Some围绕 的值构建c。在Option因为调用一个舞蹈需要FnOnce移动的所有权,这从背后是不可能self: &mut ScopeCall<F>没有它。(这样做没问题,因为析构函数只执行一次。)总而言之:struct ScopeCall<F: FnOnce()> {&nbsp; &nbsp; c: Option<F>}impl<F: FnOnce()> Drop for ScopeCall<F> {&nbsp; &nbsp; fn drop(&mut self) {&nbsp; &nbsp; &nbsp; &nbsp; self.c.take().unwrap()()&nbsp; &nbsp; }}macro_rules! expr { ($e: expr) => { $e } } // tt hackmacro_rules! defer {&nbsp; &nbsp; ($($data: tt)*) => (&nbsp; &nbsp; &nbsp; &nbsp; let _scope_call = ScopeCall {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c: Some(|| -> () { expr!({ $($data)* }) })&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; )}fn main() {&nbsp; &nbsp; let x = 42u8;&nbsp; &nbsp; defer!(println!("defer 1"));&nbsp; &nbsp; defer! {&nbsp; &nbsp; &nbsp; &nbsp; println!("defer 2");&nbsp; &nbsp; &nbsp; &nbsp; println!("inside defer {}", x)&nbsp; &nbsp; }&nbsp; &nbsp; println!("normal execution {}", x);}(与原始输出相同。)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go