2 回答

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);
}
(與原始輸出相同。)
- 2 回答
- 0 關注
- 466 瀏覽
添加回答
舉報