Yes, if you were to duplicate the code, you'd be doing the same thing as monomorphization. But it's possible that they wouldn't actually duplicate it, and do something else that would be smaller, like, casting to a void pointer or something.
The compiler can do some optimizations like this, yes, but there's a lot more work to do in this area.
Why split this into two functions? Well, you can get smaller code size this way, because you're sort of "hand-de-duplicating" the parts that aren't generic. Once we call .as_ref, everything else is actually identical, but the compiler isn't good enough at this yet to do this itself, so we do it by hand.
I wouldn't say this technique is super super common or well-known, just for the standard library (because it's used everywhere) and for folks that are sensitive to code size, like embedded or wasm people.
Additionally when you make something easy, people do it more than they would if it were annoying and manual. You might imagine that if you had to hand roll everything you'd be more cognizant of how many copy/pastes you did.
This is exactly what I meant. Thanks Steve for the clear explanation!
In the next part of the article I'll hopefully explore some drawbacks of choosing generics all the way down. Either driver would've likely perform a little better if developed fully independently, but by a smaller margin that I would've expected.
Nope; dyn is dynamic dispatch, this is statically dispatched.
It is true that dyn means you don't get monomorphization, and can help with binary sizes.
EDIT: thinking about this some more, I wanted to say that it does feel similar, but one big difference that’s easy to explain is that dyn will change the way the value is represented in memory, and this will not. Conceptually, both do “cast and then call this single function”, but in the dyn case, the cast would be to a trait object, whereas this casts directly to &Path.
The compiler can do some optimizations like this, yes, but there's a lot more work to do in this area.
As an example of something folks still do by hand sometimes, take the starts_with method on Path: https://doc.rust-lang.org/stable/std/path/struct.Path.html#m...
It looks like this:
Why split this into two functions? Well, you can get smaller code size this way, because you're sort of "hand-de-duplicating" the parts that aren't generic. Once we call .as_ref, everything else is actually identical, but the compiler isn't good enough at this yet to do this itself, so we do it by hand.I wouldn't say this technique is super super common or well-known, just for the standard library (because it's used everywhere) and for folks that are sensitive to code size, like embedded or wasm people.