2 回答
TA贡献1817条经验 获得超14个赞
(e:不要错过下面的 bluss 的回答和他们的scopedguard crate。)
通过让代码在析构函数中运行来实现这一点的正确方法,就像defer!您链接到的宏一样。除了临时测试之外,我建议使用适当的析构函数编写句柄类型,例如std::sync::Mutex通过其MutexGuard类型(由 返回lock)进行交互:无需调用unlock互斥锁本身。(显式使用析构函数处理的方法也更灵活:它可以对数据进行可变访问,而延迟方法可能不能,因为 Rust 强大的别名控制。)
无论如何,由于最近的更改,该宏现在(很多!)改进了,特别是 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(“令牌树”)的使用允许{ ... }在有多个语句时无需内部调用它(即它的行为更像“正常”控制流结构):
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 关注
- 423 浏览
添加回答
举报