我最近意识到我可以在Rust中创建局部函数(函数中的函数).这似乎是一种在不污染文件功能空间的情况下清理代码的好方法.下面我所说的局部函数与"外部"函数的比较:

fn main() {
    fn local_plus(x: i64, y: i64) -> i64 {
        x + y
    }
    let x = 2i64;
    let y = 5i64;

    let local_res = local_plus(x, y);
    let external_res = external_plus(x,y);
    assert_eq!(local_res, external_res);
}

fn external_plus(x: i64, y: i64)  -> i64 {
    x + y
}

我想知道这样做是否会对绩效产生负面影响?比如,每次运行包含函数时,Rust会重新声明函数还是会占用一些不需要的函数空间?或者它实际上没有性能方面的影响?

顺便说一句,任何关于我如何能自己找到答案的建议(通过阅读任何特定的文档集或我可以使用的工具)都是欢迎的.

推荐答案

没有影响;我判断了为两个变体生成的程序集,它是相同的.

我比较了两个版本:

"外部":

fn main() {
    let x = 2i64;
    let y = 5i64;

    let external_res = external_plus(x,y);
}

fn external_plus(x: i64, y: i64)  -> i64 {
    x + y
}

"本地":

fn main() {
    fn local_plus(x: i64, y: i64) -> i64 {
        x + y
    }
    let x = 2i64;
    let y = 5i64;

    let local_res = local_plus(x, y);
}

两者都会产生相同的asm结果(在今天的夜间发布模式):

    .text
    .file   "rust_out.cgu-0.rs"
    .section    .text._ZN8rust_out4main17hb497928495d48c40E,"ax",@progbits
    .p2align    4, 0x90
    .type   _ZN8rust_out4main17hb497928495d48c40E,@function
_ZN8rust_out4main17hb497928495d48c40E:
    .cfi_startproc
    retq
.Lfunc_end0:
    .size   _ZN8rust_out4main17hb497928495d48c40E, .Lfunc_end0-_ZN8rust_out4main17hb497928495d48c40E
    .cfi_endproc

    .section    .text.main,"ax",@progbits
    .globl  main
    .p2align    4, 0x90
    .type   main,@function
main:
    .cfi_startproc
    movq    %rsi, %rax
    movq    %rdi, %rcx
    leaq    _ZN8rust_out4main17hb497928495d48c40E(%rip), %rdi
    movq    %rcx, %rsi
    movq    %rax, %rdx
    jmp _ZN3std2rt10lang_start17h14cbded5fe3cd915E@PLT
.Lfunc_end1:
    .size   main, .Lfunc_end1-main
    .cfi_endproc


    .section    ".note.GNU-stack","",@progbits

这意味着生成的二进制文件中会有零差异(不仅仅是性能方面的差异).

更重要的是,你是否使用函数都无关紧要;以下方法:

fn main() {
    let x = 2i64;
    let y = 5i64;

    let res = x + y;
}

也会产生相同的组件.

底线是,通常情况下,函数都是内联的,不管您是在main()中声明它们还是在main()之外声明它们.

Edit:正如Shepmaster所指出的,在这个程序中没有副作用,因此两种变体生成的装配实际上与以下两种变体的装配相同:

fn main() {}

然而,两者的MIR输出也是相同的(与空白main()的MIR输出不同),因此即使存在副作用,功能位置也不会有任何差异.

Rust相关问答推荐

为什么我需要在这个代码示例中使用&

关联类型(类型参数)命名约定

当Option为None时,Option数组是否占用Rust中的内存?

我怎样才能从一个Rust 的日期中go 掉3年?

如何装箱生命周期相关联的两个对象?

防止cargo test 中的竞争条件

可以为rust构建脚本编写单元测试吗?

用于判断整数块是否连续的SIMD算法.

try 创建随机数以常量

在Rust中声明和定义一个 struct 体有什么区别

如何在AVX2中对齐/旋转256位向量?

Rust,如何从 Rc> 复制内部值并返回它?

借来的价值生命周期 不够长,不确定为什么它仍然是借来的

Rust ECDH 不会产生与 NodeJS/Javascript 和 C 实现相同的共享密钥

由特征键控的不同 struct 的集合

在 Bevy 项目中为 TextureAtlas 精灵实施 NearestNeighbor 的正确方法是什么?

如何将 Rust 字符串转换为 i8(c_char) 数组?

如何获取包裹在 Arc<> 和 RwLock<> 中的 Rust HashMap<> 的长度?

隐式类型闭包的错误生命周期推断

如何在 Rust 的泛型函​​数中同时使用非拥有迭代器和消费迭代器?