草庐IT

Rust 中类似 Golang 的延迟

coder 2023-04-30 原文

在 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,但在我的具体情况下,状态位于外部系统中。我正在编写一个将键写入键值存储的测试,我需要确保在测试结束时将其删除,无论测试中的断言是否导致 panic 。

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

还有this issue在 Rust GitHub 存储库上,但它已有三年历史,显然不再非常相关。

最佳答案

(e:不要错过下面的 bluss 的回答和他们的 scopedguard crate 。)

正确的方法是让代码在析构函数中运行,例如 defer!您链接到的宏。除了临时测试之外,我建议使用适当的析构函数编写句柄类型,例如一个与 std::sync::Mutex 交互通过其 MutexGuard 类型(由 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)* }) }
        };
    )
}

(tt hack 是必需的,因为 #5846 。)

使用泛型 tt ("token tree") 允许在没有内部 { ... } 的情况下调用它当有多个语句时(即它的行为更像一个“正常”的控制流结构):

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()()
    }
}

这也需要构建 ScopeCallSome 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);
}

(与原始输出相同。)

关于Rust 中类似 Golang 的延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29963449/

有关Rust 中类似 Golang 的延迟的更多相关文章

  1. ruby - Ruby 是否有类似于 Perl 的 "perl -d"的逐步调试器? - 2

    Ruby是否有逐步调试器,类似于Perl的“perl-d”? 最佳答案 ruby-debug(对于ruby1.8),debugger(对于ruby1.9),byebug(对于ruby​​2.0)以及trepanning系列都有一个-x或--trace选项。在调试器内部,命令setlinetrace将打开或关闭线路跟踪。这是themanualforruby-debug原来的答案已经修改,因为数据噪声文章的链接,唉,不再有效了。还添加了ruby​​-debug的后继者 关于ruby-Ruby

  2. ruby - 使对象的行为类似于 ruby​​ 中并行分配的数组 - 2

    假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje

  3. ruby-on-rails - 在所有延迟的作业之前 Hook - 2

    是否可以在所有delayed_job任务之前运行一个方法?基本上,我们试图确保每个运行delayed_job的服务器都有我们代码的最新实例,所以我们想运行一个方法来在每个作业运行之前检查它。(我们已经有了“check”方法并在别处使用它。问题只是关于如何从delayed_job中调用它。) 最佳答案 现在有一种官方方法可以通过插件来做到这一点。这篇博文通过示例清楚地描述了如何执行此操作http://www.salsify.com/blog/delayed-jobs-callbacks-and-hooks-in-rails(本文中描述

  4. 语法类似于 GitHub Flavored Markdown 的 Ruby markdown 解释器? - 2

    我使用Jekyll运行博客,并认为我会解决RedcarpetMarkdown解释器,因为它是developedandusedbyGitHub.好吧,我只是碰巧遇到了一个错误,去检查问题,然后foundthis.Maintainersays,"Asyouprobablyhavenoticed(harharharhar)Idon'thavetimetomaintainRedcarpetanymore.It'snotapriorityforme(IfindMarkdownthoroughlyboring)andit'snotapriorityforGitHub,becausewenolong

  5. ruby - Dropbox 类似 git 的服务——没有 rsync 和 inotify - 2

    关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec

  6. python - python中有没有类似于ruby的||=的表达式 - 2

    我在Ruby中遇到了一个有趣的表达式:a||="new"表示如果没有定义a,则将"new"值赋给a;否则,a将保持原样。在进行一些数据库查询时很有用。如果设置了该值,我不想触发另一个数据库查询。所以我在Python中尝试了类似的思路:a=aifaisnotNoneelse"new"失败了。我认为这是因为如果未定义a,则无法在Python中执行“a=a”。所以我能得出的解决方案是检查locals()和globals(),或者使用try...except表达式:myVar=myVarif'myVar'inlocals()and'myVar'inglobals()else"new"或try:

  7. regex - Ruby 是否有类似于 Perl 6 语法的插件? - 2

    多年来,Perl一直是我首选的编程语言工具之一。Perl6语法看起来像是一个很棒的语言特性。我想知道是否有人开始为Ruby做这样的事情。 最佳答案 如果您想在Ruby中使用实际的Perl6语法,最好的选择是Cardinal,Parrot上的ruby​​编译器。它目前尚未完成并且非常缓慢,但我非常希望它最终成为一个可行的ruby​​实现。它目前大部分处于非事件状态,等待Parrot中的一些基础架构更改以支持改进的解析速度和其他功能。 关于regex-Ruby是否有类似于Perl6语法的插件

  8. ruby - Heroku - 如何开始工作人员(延迟工作)? - 2

    我有一些使用delayed_job的小程序。在我的本地主机上一切正常,但是当我将我的应用程序部署到Heroku并单击应该由delayed_job执行的链接时,没有任何反应,“任务”只是保存到表delayed_job中。Inthisarticleonherokublog写入时,执行delayed_job表中的任务,当运行此命令时rakejobs:work。但是我怎样才能运行这个命令呢?命令应该放在哪里?在代码中,还是从终端控制台? 最佳答案 如果您正在运行Cedar堆栈,请从终端控制台运行以下命令:herokurunrakejobs:

  9. ruby - npm link 的 Ruby 类似物是什么? - 2

    如果您是Nodejs项目的源代码,则命令npmlink会以这样一种方式安装它:您所做的任何更改都适用于所有地方,而无需重新安装。npmlinkisdesignedtoinstalladevelopmentpackageandseethechangesinrealtimewithouthavingtokeepre-installingit.https://www.npmjs.org/doc/misc/npm-developers.html#link-packages是否有Ruby项目的类似物? 最佳答案 Ruby的等价物是在Gemfi

  10. ruby-on-rails - 是否有类似 'with_indifferent_access' 的数组可用于包含? - 2

    我尝试在我的应用中只使用:symbols作为关键词。我尝试在:symbol=>logic或string=>UI/languagespecific之间做出严格的决定但我也得到了每个JSON的一些“值”(即选项等),因为JSON中没有:symbols,所以我调用的所有哈希都具有“with_indifferent_access”属性。但是:数组是否有相同的东西?像那样a=['std','elliptic',:cubic].with_indifferent_accessa.include?:std=>true?编辑:将rails添加到标签 最佳答案

随机推荐