改变闭包主体中的变量



我正在写一堆断言,这些断言都涉及从列表中弹出一个值。

来自Scala背景,我自然而然地这样做了:

let mut list = List::new();
let assert_pop = |expected| assert_eq!(list.pop(), expected);

这样我就可以写assert_pop(None)assert_pop(Some(3)),而不必每次都写assert_eq!(list.pop(), None)assert_eq!(list.pop(), Some(3))

当然,借用

检查器不喜欢这一点,因为闭包基本上需要在未公开的时间内借用值,而我的其余代码则四处变异,从而违反了"如果你正在变异,则没有别名"的规则。

问题是:有没有办法解决这个问题?我是否必须编写宏,或者是否有一种时髦的内存安全方法可以解决此问题?

请注意,我知道我可以像这样定义闭包:

let_assert_pop = |lst: &mut List, expected| assert_eq!(lst.pop(), expected);

但这不会那么干涩,因为我必须在每次通话中传递一个&mut list作为第一个参数。

他们的关键是将闭包定义为 mut,因为它需要一个可变的引用。

这有效:

let mut v = vec![1, 2];
let mut assert_pop = |expected| assert_eq!(v.pop(), expected);
assert_pop(Some(2));
assert_pop(Some(1));
assert_pop(None);

请注意,pop闭包是可变借用的,因此如果您想在之后使用该列表,则必须限定其范围:

let mut v = vec![1,2];
{
    let mut assert_pop = |expected| assert_eq!(v.pop(), expected);
    assert_pop(Some(2));
    v.push(33); // ERROR: v is borrowed mutably...
}
v.push(33); // Works now, since pop is out of scope.
<</div> div class="one_answers">

我不会直接回答你的问题(这个问题已经回答得很好),而是要解决你的其他问题:

  • 只需写assert_pop(None)assert_pop(Some(3))
  • 内存安全方式解决方案
  • 不要通过&mut list

要解决所有这些问题,请不要使用闭包,只需创建一个新类型:

type List<T> = Vec<T>;
struct Thing<T>(List<T>);
impl<T> Thing<T> {
    fn assert_pop(&mut self, expected: Option<T>) 
        where T: PartialEq + std::fmt::Debug,
    {
        assert_eq!(self.0.pop(), expected);
    }
}
fn main() {
    let list = List::new();
    let mut list = Thing(list);
    list.0.push(1);
    list.assert_pop(Some(1));
    list.assert_pop(None);
    // Take it back if we need to
    let _list = list.0;
}

最新更新