我有以下代码段:
import {connect} from "react-redux";
import {updateStuf} from "./actions/projectActions";
class App extends Component {
componentWillMount() {
this.props.updateStuf();
}
render() {
const {stuff} = this.props;
return (
<div className="stuff">
</div>
);
}
}
const mapStateToProps = (state) => {
return {
stuff: state.project.stuff
}
}
export default connect(mapStateToProps, {updateStuf})(App);
疑问1
我什至无法执行关闭操作
疑问2
...但是,只要我通过fn f<T: FnOnce() -> u32>(c: T) {
println!("Hello {}", c());
}
fn main() {
let mut x = 32;
let g = move || {
x = 33;
x
};
g(); // Error: cannot borrow as mutable. Doubt 1
f(g); // Instead, this would work. Doubt 2
println!("{}", x); // 32
}
调用该闭包,我就可以多次调用它。有趣的是,如果我将其声明为f
,则会收到与疑问1相同的错误。
疑问3
FnMut
在self
,Fn
和FnMut
特征定义中指的是什么?那是封闭本身吗?还是环境?
例如。从文档中:
FnOnce
答案 0 :(得分:2)
您在这里处理两种不同类型的闭包-FnOnce
和FnMut
。两种类型的闭包都有不同的调用约定。
如果您将闭包定义为
let mut x = 32;
let g = move || {
x = 33;
x
};
编译器将推断闭包的类型为FnMut
。尽管闭包返回拥有的变量x
,但由于x
为Copy
,它仍然可以多次调用,因此编译器选择FnMut
作为最通用的类型。
在调用FnMut
闭包时,闭包本身通过可变引用传递。这就解释了您的第一个问题-除非将其变为可变状态,否则直接调用g
无效,因为否则将无法对其进行可变引用。我还在这里隐式回答了您的第三个问题-self
特征的调用方法中的Fn
指的是闭包本身,可以将其视为包含所有捕获变量的结构。
调用f(g)
时,您将FnMut
闭包g
作为FnOnce
闭包传递给f()
。之所以允许这样做,是因为所有FnOnce
都是FnMut
的特征,因此每个实现FnMut
的闭包也都实现了FnOnce
。现在,闭包已转换为FnOnce
,也根据FnOnce
调用约定对其进行了调用:
pub trait FnOnce<Args> {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
在这种情况下,闭包由 value 传递,因此调用会消耗闭包。您可以放弃拥有的任何价值的所有权-它不需要可变就可以起作用。
通过g
调用f()
可以多次调用的原因是g
是Copy
。它仅捕获单个整数,因此可以根据需要复制多次。每次对f()
的调用都会创建一个g
的新副本,当在f()
内部调用该副本时会使用该副本。
答案 1 :(得分:2)
需要一些有关Fn*
特质家族的基础知识,以了解闭包实际上是如何工作的。您具有以下特征:
FnOnce
,顾名思义,只能运行一次。如果查看docs页面,将会看到trait定义与您在问题中指定的定义几乎相同。不过,最重要的是以下内容:“调用”函数采用self
,这意味着它消耗实现FnOnce
的对象,就像任何将self
用作trait的函数一样参数,它获取对象的所有权。 FnMut
,它允许捕获的变量发生突变,换句话说,它需要&mut self
。这意味着,当您创建move || {}
闭包时,它将引用的所有超出闭包范围的变量都移到闭包的对象中。闭包的对象具有无法命名的类型,这意味着它对于每个闭包都是唯一的。这确实迫使用户采用某种可变的闭包版本,因此&mut impl FnMut() -> ()
或mut x: impl FnMut() -> ()
Fn
,通常被认为是最灵活的。这允许用户获取实现特征的对象的不变版本。该特征的“调用”函数的函数签名是这三个特征中最简单的理解,因为它仅引用闭包,这意味着在传递或调用闭包时不必担心所有权。 要解决您的个人疑问:
move
放入闭包中时,该变量现在由闭包拥有。本质上,编译器生成的内容类似于以下伪代码:struct g_Impl {
x: usize
}
impl FnOnce() -> usize for g_Impl {
fn call_once(mut self) -> usize {
}
}
impl FnMut() -> usize for g_Impl {
fn call_mut(&mut self) -> usize {
//Here starts your actual code:
self.x = 33;
self.x
}
}
//No impl Fn() -> usize.
默认情况下,它调用FnMut() -> usize
实现。
Copy
的捕获变量均为Copy
,这意味着生成的闭包将被复制到f
中,因此f
最终占据其中的Copy
。当您将f
的定义改为使用FnMut
时,会出现错误,因为您面临着与怀疑1类似的情况:您正试图调用接收&mut self
的函数而您已将参数声明为c: T
,而不是mut c: T
或c: &mut T
,在&mut self
眼中,这两个参数都符合FnMut
的要求。 self
参数是闭包本身,它已将某些变量捕获或移动到自身中,因此它现在拥有它们。