给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();
}
Run Code Online (Sandbox Code Playgroud)
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):警告:注意:捕获的变量对块上定义的匿名生命#1有效在3:0
如果你alt_make_counter()现在看一下这个函数,它的生命周期state应该基本上会产生相同的错误信息吧?如果代码捕获了闭包的状态,那么指针是否被传入或者变量是否绑定在函数内部无关紧要,对吧?但很明显,这2个案例神奇地不同.
谁可以解释,为什么它们不同(错误,特征,深刻见解,......?),如果有一个简单的规则可以采用,这可以防止在这些问题上不时浪费时间?
小智 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 })
}
Run Code Online (Sandbox Code Playgroud)
问题是关闭在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 })
}
Run Code Online (Sandbox Code Playgroud)
请注意,'a它也附加到FnMut() -> u32(虽然使用不同的语法,因为它是一个特征).
避免此类问题的最简单规则是在引发问题时不使用引用.这种关闭没有充分理由借用它的状态,所以不要这样做.我不知道你是否属于这个,但我看到一群人的印象&mut是改变某些东西的主要或唯一方式.那是错的.您可以按值存储它,然后直接通过将其存储或者包含它的较大结构直接变换到标记为的局部变量中mut.只有当突变的结果需要与其他代码共享并且您不能将新值传递给该代码时,可变引用才有用.
当然,有时需要以复杂的方式处理参考文献.不幸的是,似乎没有一种快速简便的方法可以学会自信地处理这些问题.这是一个很大的教育挑战,但到目前为止,似乎每个人都只是挣扎了一段时间,然后随着他们变得更有经验,逐渐减少了问题.不,没有一个简单的规则可以解决所有生命中的困境.
1返回类型必须FnMut在所有情况下.您刚才没有收到错误,因为您当前的错误发生在编译的早期阶段.