我正在编写一堆断言,这些断言都涉及从列表中弹出一个值。
来自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);
但那不会是 DRY ,因为我必须在每个电话中传入&mut list
作为第一个参数。
答案 0 :(得分:2)
他们的关键是将闭包定义为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.
答案 1 :(得分:1)
我不是直接回答你的问题(well-enough answered already),而是解决你的其他问题:
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;
}