Are there any negative consequences for using local functions in Rust?

I recently realized that I can create local functions in Rust (a function inside a function). It seems like a good way to clear my code without polluting the functional space of the file. A small sample of what I mean below on the local function vs the "external" function:

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 } 

I was wondering if there are any negative consequences to this? How does Rust redeclare a function or take up some unwanted amount of function space each time it starts a function? Or does it literally not affect performance?

In any case, any advice on how I could find an answer for myself (either by reading any specific set of documents, or tools that I could use), would be welcome.

+6
source share
2 answers

There is no influence; I checked the assembly generated for both options and it is identical.

Two versions that I compared:

"external":

 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 } 

"local":

 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); } 

And both give the same asm result (release mode in today's night mode):

  .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 

This means that there will be zero difference in the generated binary format (not only performance).

Also, it doesn't matter if you use a function; following approach:

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

Also gives the same build.

The bottom line is that in the general case, functions are embedded regardless of whether you declare them in main() or outside it.

Edit : as Shepmaster noted, there are no side effects in this program, so the generated assembly for both options is actually the same as for:

 fn main() {} 

However, the MIR output for both is the same (and different from one for the empty main() ), so there should be no difference coming from the location of the function, even if side effects were present.

+5
source

In any case, any advice on how I could find an answer for myself (either by reading any specific set of documents, or tools that I could use), would be welcome.

Do you know the Rust site ?

Enter your code, click "LLVM IR", "Assembly" or "MIR" instead of "Run", and you will see what a low-level view released for the specified code is.

I personally prefer LLVM IR (I'm used to reading it with C ++), which is still at a fairly high level than the assembly, which is still a mail language.

I was wondering if there are any negative consequences to this?

That's a very difficult question; actually.

the difference between declaring a function locally or externally in Rust is one area . Declaring it locally simply reduces its volume. Nothing more .

However ... scope and use can have serious compilation consequences.

A function that is used only once, for example, is much more likely than a function that is used 10 times. The compiler cannot easily estimate the number of uses of the pub function (unlimited), but it has excellent knowledge of local or non-t20> functions. And regardless of whether the function is built-in or not, it can significantly affect the performance profile (worse or better).

Thus, by reducing the volume and thereby limiting use, you encourage the compiler to consider your function for inline (if your character is not cold).

On the other hand, since the scope is reduced, it cannot be divided (obviously).

So ... what?

Follow the usage: identify the element in the maximum possible area.

This is encapsulation : now, the next time you need to change this part, you will definitely know the area of ​​impact.

Some have confidence in Rust; it will not introduce overhead if it can avoid it.

+6
source