亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Rust 中類似 Golang 的 defer

Rust 中類似 Golang 的 defer

Go
aluckdog 2021-10-04 13:29:54
在 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 存儲庫上也有這個問題,但它已經三年了,顯然不再重要了。
查看完整描述

2 回答

?
大話西游666

TA貢獻1817條經驗 獲得超14個贊

(e:不要錯過下面的 bluss 的回答和他們的scopedguard crate。)


通過讓代碼在析構函數中運行來實現這一點的正確方法,就像defer!您鏈接到的宏一樣。除了臨時測試之外,我建議使用適當的析構函數編寫句柄類型,例如std::sync::Mutex通過其MutexGuard類型(由 返回lock)進行交互:無需調用unlock互斥鎖本身。(顯式使用析構函數處理的方法也更靈活:它可以對數據進行可變訪問,而延遲方法可能不能,因為 Rust 強大的別名控制。)


無論如何,由于最近的更改,該宏現在(很多?。└倪M了,特別是 pnkfelix 的聲音通用刪除工作,這消除了#[unsafe_destructor]. 直接更新將是:


struct ScopeCall<F: FnMut()> {

    c: F

}

impl<F: FnMut()> Drop for ScopeCall<F> {

    fn drop(&mut self) {

        (self.c)();

    }

}


macro_rules! defer {

    ($e:expr) => (

        let _scope_call = ScopeCall { c: || -> () { $e; } };

    )

}


fn main() {

    let x = 42u8;

    defer!(println!("defer 1"));

    defer!({

        println!("defer 2");

        println!("inside defer {}", x)

    });

    println!("normal execution {}", x);

}

輸出:


normal execution 42

defer 2

inside defer 42

defer 1

雖然,它在語法上會更好,因為:


macro_rules! expr { ($e: expr) => { $e } } // tt hack

macro_rules! defer {

    ($($data: tt)*) => (

        let _scope_call = ScopeCall { 

            c: || -> () { expr!({ $($data)* }) }

        };

    )

}

(tt hack由于#5846 ,這是必要的。)


泛型tt(“令牌樹”)的使用允許{ ... }在有多個語句時無需內部調用它(即它的行為更像“正?!笨刂屏鹘Y構):


defer! {

    println!("defer 2");

    println!("inside defer {}", x)

}

此外,為了獲得延遲代碼可以對捕獲的變量執行的操作的最大靈活性,可以使用FnOnce代替FnMut:


struct ScopeCall<F: FnOnce()> {

    c: Option<F>

}

impl<F: FnOnce()> Drop for ScopeCall<F> {

    fn drop(&mut self) {

        self.c.take().unwrap()()

    }

}

這還需ScopeCall要用Some圍繞 的值構建c。在Option因為調用一個舞蹈需要FnOnce移動的所有權,這從背后是不可能self: &mut ScopeCall<F>沒有它。(這樣做沒問題,因為析構函數只執行一次。)


總而言之:


struct ScopeCall<F: FnOnce()> {

    c: Option<F>

}

impl<F: FnOnce()> Drop for ScopeCall<F> {

    fn drop(&mut self) {

        self.c.take().unwrap()()

    }

}


macro_rules! expr { ($e: expr) => { $e } } // tt hack

macro_rules! defer {

    ($($data: tt)*) => (

        let _scope_call = ScopeCall {

            c: Some(|| -> () { expr!({ $($data)* }) })

        };

    )

}


fn main() {

    let x = 42u8;

    defer!(println!("defer 1"));

    defer! {

        println!("defer 2");

        println!("inside defer {}", x)

    }

    println!("normal execution {}", x);

}

(與原始輸出相同。)


查看完整回答
反對 回復 2021-10-04
  • 2 回答
  • 0 關注
  • 466 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號