1. 13
    1. 1

      Isn’t this basically type erasure? Only you use inner functions instead of something like std::function in C++ or whatever.

      1. 2

        Not really, because no type information is being lost. The AsRef<Path> bound in the example represents the ability to do a conversion into a &Path, and even in the version without an inner function, you need to call .as_ref() before being able to use the value. So all that’s happening here is that the conversion is being done ahead of time.

        1. 1

          Forgive me if I’m not understanding this right, but Path here is a trait, no? So the inner function just takes a reference to “something that implements Path”. That sounds like type erasure to me, in that it removes all information related to a type except for the fact that it can do what a Path can do.

          In fact, unless rustc optimizes this, wouldn’t this generate a new function per concrete type that calls the outer function?

          1. 2

            No, Path is a struct, while AsRef<Path> is a trait. So the inner function is taking a reference to a concrete type, while the outer function is the one where the only thing it knows about its parameter is that it’s “something that can be converted to a &Path”.

            1. 1

              What does “convert” mean in this context? I am not that well-versed in Rust, sorry.

              1. 1

                No need to apologise! “Convert” here just means that if you have something that implements AsRef<Path>, you can call .as_ref() on it and you’ll get back a &Path.

                So the meaning of “convert” depends somewhat on how the trait is implemented by the type you’re calling it on. In the case of PathBuf, which is just a slightly different representation of a path that already can automatically dereference to a Path, it’s a simple as that: it dereferences itself and returns a &Path. But in the case of String, its implementation of AsRef<Path> actually creates a new Path from the string and returns a reference to it.

          2. 1

            In fact, unless rustc optimizes this, wouldn’t this generate a new function per concrete type that calls the outer function?

            This is correct, though. As far as I understand, the point of this optimisation is just to reduce the size of the outer functions that are generated, by moving some of the function body into the inner function.

            1. 2

              (Blog post author here!) Also good to note that the outer generic function is now extremely small and is likely to be inlined into the caller, leaving only a call to the non-generic inner function.

    2. 1

      It seems to me you could do this in the compiler by just making sure duplicated (by bytecode) morphs of generic functions are coalesced after generation? I’m certainly no smarter than the rust devs, so what am I missing that makes this difficult?