给VisualRust另一个尝试,看看他们有多远,我写了几行代码。像往常一样,代码使我在stackoverflow上写一个问题......
首先,请稍后阅读我的问题:
fn make_counter( state : &mut u32 ) -> Box<Fn()->u32>
{
Box::new(move || {let ret = *state; *state = *state + 1; ret })
}
fn test_make_counter() {
let mut cnt : u32 = 0;
{
let counter = make_counter( & mut cnt );
let x1 = counter();
let x2 = counter();
println!("x1 = {} x2 = {}",x1,x2);
}
}
fn alt_make_counter ( init : u32 ) -> Box<Fn()->u32> {
let mut state = init;
Box::new(move || {let ret = state; state = state + 1; ret })
}
fn test_alt_make_counter() {
let counter = alt_make_counter( 0u32 );
let x1 = counter();
let x2 = counter();
println!("x1 = {} x2 = {}",x1,x2);
}
fn main() {
test_make_counter();
test_alt_make_counter();
}
make_counter()
和alt_make_counter()
之间的区别在于,在一种情况下,状态是指向传递给函数的可变u32的指针,而在另一种情况下,它是在内部定义的可变u32功能。由于test_make_counter()函数清楚地显示,因此闭包的寿命比变量cnt
长。即使我删除了test_make_counter()
内的块,它们仍然具有相同的生命周期。使用该块,counter
将在cnt
之前消亡。然而,Rust抱怨道:
src \ main.rs(4,2):错误:捕获的变量
state
不会超过封闭的闭包 src \ main.rs(3,1):警告:注意:捕获的变量对3:0块上定义的匿名生命#1有效
如果你现在查看alt_make_counter()
函数,state
的生命周期应该基本上会导致相同的错误消息,对吧?如果代码捕获了闭包的状态,那么指针是否被传入或者变量是否绑定在函数内部无关紧要,对吧?但很明显,这2个案例神奇地不同。
谁可以解释,为什么它们不同(错误,特征,深刻的洞察力......?),如果有一个简单的规则可以采用,这可以防止在这些问题上不时浪费时间?
答案 0 :(得分:5)
不同之处在于使用局部变量与使用参数。参数完全是普通的本地人。实际上,此版本的alt_make_counter
可以使用 1 :
fn alt_make_counter (mut state: u32) -> Box<FnMut() -> u32> {
Box::new(move || {let ret = state; state = state + 1; ret })
}
问题是make_counter
中的结束关闭了&mut u32
而不是u32
。它没有自己的状态,它在其他地方使用整数作为其临时空间。因此,它需要担心该位置的生命周期。函数签名需要传达闭包只能在它仍然可以使用传入的引用时才能工作。这可以用生命周期参数表示:
fn make_counter<'a>(state: &'a mut u32) -> Box<FnMut() -> u32 + 'a> {
Box::new(move || {let ret = *state; *state = *state + 1; ret })
}
请注意,'a
也会附加到FnMut() -> u32
(虽然语法不同,因为它是一种特质)。
避免此类问题的最简单规则是在引发问题时不使用引用。这种关闭没有充分理由借用它的状态,所以不要这样做。我不知道你是否属于这个,但我看到一群人认为&mut
是改变某些东西的主要或唯一方式。 这是错误的。您可以按值存储它,然后直接通过将其存储或包含它的较大结构直接改变为标记为{{1}的局部变量}。只有当突变的结果需要与其他代码共享时,可变的引用才有用,并且您不能将新值传递给该代码。
当然,有时需要以复杂的方式处理参考文献。不幸的是,似乎并不是一种快速而简单的方法来学习如何自信地处理这些问题。这是一个很大的教育挑战,但到目前为止,似乎每个人都只是挣扎了一段时间,然后随着他们变得更有经验,逐渐减少了问题。不,没有一个简单的规则可以解决所有生命中的困境。
1 在所有情况下,返回类型必须为mut
。你刚才没有得到错误,因为你当前的错误发生在编译的早期阶段。