< 返回版块

sstudioer 发表于 2021-02-23 11:07

fn func(){
	let mut a = 0;
	for i in 1..=10{
	      a = (|x| a+x)(i);  // 这个匿名函数 |x| a+x 是否会重复的声明10次并调用?
	}
}

fn func(){
 	let mut  a = 0;
	for i in 1..=10{
		fn fun() -> i32 {return 100;}  // 这个局域的函数会否声明10次?
		a += fun();
	}
}

A closure expression produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured variables. For instance, the following closure:

评论区

写评论
Aya0wind 2021-02-23 12:27

是的,不内联也会把context找个内存块存着,每次循环都只会访问这一块。另外贴主这个例子连内存块也不需要,不捕获外部变量的匿名函数在Rust里等价于函数(函数指针)的,编译之后就算不内联也是直接一个call,也不会占额外内存。

👇
johnmave126: 就算不内联也不会

仔细想想,如果循环长度是一个变量,假设每一次都需要一个新的内存段用来储存匿名函数,编译期是无法知道需要多少个匿名函数的。难道程序还能JIT现场在内存里编译匿名函数?

事实上,编译器是block level的,会先计算出一个CFG(control flow graph),匿名函数是一个block,不会被复制很多份。

对于第一个情况换个问题就是,在不考虑优化的情况下,运行期这样是否会产生多个闭包实例(本质上是context+函数地址),答案是会的。

对于第二个情况,运行期也不会,因为fn定义的位置只影响可见性,编译时会被提升到外层,函数调用就是普通的函数调用。

johnmave126 2021-02-23 12:09

就算不内联也不会

仔细想想,如果循环长度是一个变量,假设每一次都需要一个新的内存段用来储存匿名函数,编译期是无法知道需要多少个匿名函数的。难道程序还能JIT现场在内存里编译匿名函数?

事实上,编译器是block level的,会先计算出一个CFG(control flow graph),匿名函数是一个block,不会被复制很多份。

对于第一个情况换个问题就是,在不考虑优化的情况下,运行期这样是否会产生多个闭包实例(本质上是context+函数地址),答案是会的。

对于第二个情况,运行期也不会,因为fn定义的位置只影响可见性,编译时会被提升到外层,函数调用就是普通的函数调用。

Aya0wind 2021-02-23 11:35

不会,没必要,你这里的匿名函数都没有捕获外部变量,就等于一个普通的函数,相当于直接调用函数。 甚至你这个函数太短了,完全可以内联掉,在开了优化之后估计连函数调用都没了。 直接等价于:

let mut a = 0;
for i in 1..=10{
    a +=i;
}

另外加上你这里都是常数,真正开优化其实应该优化成这一句话:

let mut a=55;
johnmave126 2021-02-23 11:34

在godbolt上看了一下,不会

1 共 4 条评论, 1 页