理解Rust闭包:为什么他们不断地持有可变引用?

xfb7svmp  于 2023-05-18  发布在  其他
关注(0)|答案(2)|浏览(181)

我目前正在学习Rust中的闭包,并试图理解为什么它们在最后一次使用之前一直保持引用。
下面是一个例子:

fn borrows_mutably_func(list: &mut Vec<i32>) {
    list.push(4);
}

fn main() {
    let mut list = vec![1, 2, 3];
    println!("Before defining: {:?}", list);

    borrows_mutably_func(&mut list);
    println!("After calling first (func): {:?}", list);
    borrows_mutably_func(&mut list);
    println!("After calling second (func): {:?}", list);
    
    
    let mut borrows_mutably_closure = || list.push(5);
    
    borrows_mutably_closure();
    println!("After calling first (closure): {:?}", list);
    borrows_mutably_closure();
    println!("After calling second (closure): {:?}", list);
}

在这段代码中,borrows_mutably_func是一个函数,它通过向list添加一个元素来改变它。这个函数可以被多次调用而不会出现任何问题。但是,当我尝试使用闭包(borrows_mutably_closure)复制此行为时,代码无法编译。
根据我的理解,出现这个问题是因为闭包通过一个可变引用捕获list,并持续保留这个引用直到它最后一次使用。这与函数不同,后者在每次调用后释放引用。
我的问题是:为什么Rust对闭包强制执行这种行为?为什么闭包不能在第一次使用后释放可变引用(就像函数那样),并在第二次使用前重新获取它?这种方法对我来说似乎更直观,因为它允许闭包的行为与函数类似,而无需持续持有可变引用。任何见解将不胜感激!

1zmg4dgp

1zmg4dgp1#

我的问题是:为什么Rust对闭包强制执行这种行为?为什么闭包不能在第一次使用后释放可变引用(就像函数那样),并在第二次使用前重新获取它?
因为这不可行。闭包本质上是一个存储它所关闭的所有东西的结构。所以在第13行,你实际上示例化了一个类似于以下的结构:

struct BorrowsMutablyClosure<'a> {
    list: &'a mut Vec<i32> 
}
impl BorrowsMutablyClosure<'_> {
    fn call_mut(&mut self) {
        self.list.push(5);
    }
}

函数是完全不同的情况,你在每次调用时 * 传入 * 引用,这就是函数可用引用的方式。
你可以对闭包做同样的事情,如果你把引用作为参数传入,它就不需要在内部存储引用。
这种方法对我来说似乎更直观
但这说不通。从创建闭包到销毁闭包的那一刻,可变引用必然是突出的,因为闭包需要存储它。没有机制释放闭包,然后重新获取可变引用。

omtl5h9j

omtl5h9j2#

闭包的行为实际上与可变引用的行为相同─不同之处在于,在第一个示例中,您不是创建一个可变引用,然后“重新获取”它,而是创建两个单独的可变引用,它们的生存期都不会与list的另一次使用重叠。
如果你创建一个可变引用,其生存期跨越两个调用,你会得到与第二个例子相同的错误(使用闭包):

let list_ref = &mut list;

borrows_mutably_func(list_ref);
// [E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
println!("After calling first (func): {:?}", list);
borrows_mutably_func(list_ref);

基本上,Rust不会跟踪一个可变引用是否在它仍然存在的时候被“释放”;只要可变引用仍然有效,借用检查器就要求不访问相同的值,即使该可变引用可以被“释放”然后被“重新获取”。
要满足借用检查器的要求,您必须使用两个具有不同生存期的可变引用来显式释放和重新获取可变引用,就像第一个示例所做的那样。

相关问题