Golang中的Golang-like延迟

时间:2015-04-30 09:04:04

标签: go rust

在Go中,您可以使用syms A Epsilon0 k1 k2 e s d R s f T c0 v0 v An = 0.5*10^(-9); Epsilon0n = 8.85*10^-12; k1n = 10; k2n = 10; en = 1.6*10^-19; sn = 1.8*10^(-9);%m gap size% dn = 3*sn; % total distance Rn = 1; c0n=(An*Epsilon0n*k1n)/dn; t1=0:0.1:5; fn=1000; v0n=7.5; Tn=5/f; vn=v0n*sin(2*3.14*fn*t1); plot(t1,vn); inits = 'q(0)=(20*10^(-20)),Q(0)=0'; [q,Q] = dsolve('Dq=((v/R)-(1/R)*(((d*q)+(s*Q))/(c0*d)))','DQ= (((q+Q)/(A*Epsilon0*k1))*s)',inits); qn1=vpa(subs(q,{A,Epsilon0,k1,k2,e,s,d,R,c0}, {An,Epsilon0n,k1n,k2n,en,sn,dn,Rn,c0n}),3); i=1; while i<length(t1); qn2(i)=subs(qn1, v, vn(i)); i=i+1; end t2=t1(1:50); plot(t2,qn2) 关键字在当前函数返回时执行函数,类似于其他语言中的传统defer关键字。无论整个功能体发生什么,这对于清理状态都很有用。以下是Go博客的一个例子:

finally

如何在Rust中实现此功能?我知道RAII,但在我的具体情况下,状态是在外部系统中。我正在编写一个将密钥写入键值存储的测试,我需要确保在测试结束时将其删除,无论测试中的断言是否会引起恐慌。

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

Rust GitHub存储库中还有this issue,但它已经三年了,显然不再那么相关了。

2 个答案:

答案 0 :(得分:20)

e:不要错过bluss的答案以及下面的scopedguard箱子。)

通过在析构函数中运行代码(如链接到的defer!宏)来实现此目的的正确方法。除了临时测试之外,我建议使用适当的析构函数编写句柄类型,例如:一个人通过其MutexGuard类型(由std::sync::Mutex返回)与lock进行互动:无需在互斥锁本身上调用unlock。 (显式句柄与析构函数方法也更灵活:它具有对数据的可变访问权限,而延迟方法可能无法实现,因为Rust具有强大的别名控制。)

在任何情况下,由于最近的变化,特别是pnkfelix的sound generic drop work,这个宏现在(很多!)得到了改善,这消除了#[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)* }) }
        };
    )
}

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

当存在多个语句时,使用泛型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 cOption舞蹈是必需的,因为调用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);
}

(与原始输出相同。)

答案 1 :(得分:11)

我使用以下内容作为范围保护。它使用Deref特征来提供共享和优先级。可移动地访问受保护的值,而不将其移出(这将使警卫无效!)

我的用例是在程序退出时正确地重置终端,即使恐慌:

extern crate scopeguard;
use scopeguard::guard;

// ... terminal setup omitted ...

// Use a scope guard to restore terminal settings on quit/panic
let mut tty = guard(tty, |tty| {
    // ... I use tty.write() here too ...
    ts::tcsetattr(tty.as_raw_fd(), ts::TCSANOW, &old_attr).ok();
});
game_main(&mut tty).unwrap();   // Deref coercion magic hands off the inner &mut TTY pointer here.

模块scopeguard.rs:

use std::ops::{Deref, DerefMut};

pub struct Guard<T, F> where
    F: FnMut(&mut T)
{
    __dropfn: F,
    __value: T,
}

pub fn guard<T, F>(v: T, dropfn: F) -> Guard<T, F> where
    F: FnMut(&mut T)
{
    Guard{__value: v, __dropfn: dropfn}
}

impl<T, F> Deref for Guard<T, F> where
    F: FnMut(&mut T)
{
    type Target = T;
    fn deref(&self) -> &T
    {
        &self.__value
    }

}

impl<T, F> DerefMut for Guard<T, F> where
    F: FnMut(&mut T)
{
    fn deref_mut(&mut self) -> &mut T
    {
        &mut self.__value
    }
}

impl<T, F> Drop for Guard<T, F> where
    F: FnMut(&mut T)
{
    fn drop(&mut self) {
        (self.__dropfn)(&mut self.__value)
    }
}

这是现在的箱子scopeguard on crates.io.