61

I'm new to Rust and have been looking through the source code a bit, and found this:

#[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
        File::create(path)?.write_all(contents)
    }
    inner(path.as_ref(), contents.as_ref())
}

Is there any reason this function defines an inner function like this? Why not just write:

File::create(path.as_ref())?.write_all(contents.as_ref())
Challe
  • 711
  • 3
  • 7

1 Answers1

71

Monomorphization costs.

write(), like most filesystem functions in Rust, takes AsRef<Path> instead of Path, for convenience (to allow you to pass e.g. a &str). But that also has a cost: it means that the function will be monomorphized and optimized separately for each type, while there is no real need for that. While it is very likely that LLVM will deduplicate all those instances, the time used for optimizing them is still wasted compile time.

To mitigate this cost, it calls an inner, non-generic function that does all the heavy lifting. The outer function contains only the necessarily-generic code - the conversion to Path.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • 7
    Wait. A nested function defined inside a generic function is only instantiated once? How does that work? – TLW May 15 '22 at 17:30
  • (As opposed to generated every time and deduped later.) – TLW May 15 '22 at 17:31
  • 22
    @TLW Nested functions aren't closures: they don't have access to the outer function's environment (parameters [including type parameters] and local variables from the outer function). Thus, `inner` is not a generic function itself. The main difference between a nested function and a non-nested function is that the nested function is only in scope within the outer function. – Francis Gagné May 15 '22 at 17:56
  • 3
    @TLW the nested function, despite looking like it's in the scope of the parent function, does not actually have access to it's generic parameters – DreamConspiracy May 15 '22 at 17:56
  • Will there be an additional function call cost then? Or is the compiler going to optimize it by inlining `write`? – Daniel Aug 18 '22 at 18:11
  • @Daniel Either it, or `inner()`, or both. Generally we trust the compiler's inlining judgment, and very small functions like that tends to be inlined very commonly. – Chayim Friedman Aug 18 '22 at 23:26