首页 > 解决方案 > 有条件地捕获闭包中的变量以实现自定义控制

问题描述

我正在尝试在 Rust 中实现自定义控制结构。例如,假设出于某种原因,我想将自定义if语句实现为函数。该函数将采用一个条件,一个代表真分支的闭包和一个代表假分支的闭包。根据条件,我会称真分支闭包或假分支闭包。

它看起来像这样:

pub fn fun_if<O>(
    cond: bool,
    mut tbranch: impl FnMut() -> O,
    mut fbranch: impl FnMut() -> O,
) -> O {
    if cond {
        tbranch()
    } else {
        fbranch()
    }
}

这个实现的问题是真正的闭包不能可变地借用与假闭包相同的变量:

let mut test = 0;
fun_if(true, || test = 1, || test = 2)
                ^^^^         ^^^^ ! error !

但是,rustif语句足够聪明,知道真假分支永远不会被调用在一起。以下编译得很好:

let mut test = 0;
if true {
    test = 1
} else {
    test = 2
}

我的问题是 if 的行为是否可以在 rust 中使用函数和不安全代码进行复制。

(这是一个稍微做作的例子,如果有人感兴趣,我很乐意提供真实的例子。我在这里问的是核心思想。)

标签: rustunsafe

解决方案


除了 Stargateurs 链接,这在一般情况下是绝对正确的,我想指出这个具体问题可以很容易地用宏解决。这是我对这个问题的看法。

macro_rules! macro_if {
    ($cond:expr, $t:stmt, $f:stmt) => {
        if ($cond) {
            $t
        } else {
            $f
        }
    };
}

示例用法

let return_val = macro_if!(
        !cond,
        {
            test = 1;
            0
        },
        {
            test = 2;
            5
        }
    );

由于宏是在编译时评估的,因此该解决方案支持您的用例,同时仍保留 rust 编译器安全地推理您的代码的能力。虽然unsafe在这个简单的示例中使用可能是合理和正确的,但更复杂的情况可能会很快导致您的实现中出现不健全的错误。我们不希望这种情况发生。

这种方法的缺点是它可能会破坏宏调用中的自动完成功能。至少对我来说,宏并不能很好地与 rls 协调(还)。


推荐阅读